C言語の学習で、関数での文字列の扱いははまりどころじゃね?
それともヲイラがバカなだけ?
もちろん独学なわけだが、Cではまったのは、他の関数から文字列を返り値として受け取るときの方法かな。あ、ちなみにここで言う文字列とは、文字の配列と同義なり。
つまり、C言語では、関数が文字列を返すことはできないのですよ!全てはこれに尽きる。文字列の先頭アドレスは返すことができますけどね。これには本当にはまりました。。。文字列という型がないのが、他の普段使っている言語と違うところでしたね。ってか、当然と言えば、当然なのですけど、頭の中では「文字列を返す」というスキーマができてしまっているので、当時はchar型の配列の先頭アドレスを返すといったことがイメージできませんでした。
つまり、こんなことをしちゃうわけ。
char *getMyString()
{
char myString[] = "hogehoge";
return myString;
}
int main()
{
char *hoge;
hoge = getMyString();
printf("%s\n", hoge);
return 0;
}
もちろん上のは動かない。
まぁ、
初級C言語Q&A(2) – Q 【文字列を戻す関数】
や
C言語講座:Bug4.html
を見ながらなんとかそのレベルを脱出しましたけどさ。しかし、このレベルを脱出するのに2年くらいかかった。まぁ、たまーにしか勉強していなかったけどさ。やっぱり近くにわかった人がいて聞けたら2年もかからなかっただろうなー。つーか、時間かかり杉。。。orz
つまり、自動変数で文字列を確保したところで、その関数の返り値は、文字の配列の先頭アドレスなので、ダメなんですよ。そして、その解決方法としてstaticを付ければいいって書いてあるけど、それは嫌なのよ。なんつーか、その方法は生理的に受け付けん。
ということで、残されたのは次の二つとなる。
- 関数の呼び出し元で文字の配列を予め確保しておく(静的に確保しても良いし、mallocで確保しても良い)。そして、そのサイズと一緒に引数にしてにその関数を呼び出す。
- 関数の中でmallocする。あとでfreeをする。
つーわけで上の動かないサンプルを動くようにするには、こうしてみる。ちなみにここで、mallocで失敗したときのことは考えていない。 (追記:Kさんの指摘の通りstrcpyはサイズは積極的にサンプルでも使わない方が良さそうですね。ここは、hogehogeという8文字ということで許してちょんまげ。)
char *getMyString()
{
char *myString;
myString = malloc(8 + 1);
strcpy(myString, "hogehoge");
return myString;
}
int main()
{
char *hoge;
hoge = getMyString();
printf("%s\n", hoge);
free(hoge);
return 0;
}
まぁ、いろいろ考えた結果、自分に合ったスタイルで書くのが望ましいと考えていたところ、構造体をクラスのように使うのが私にとっては一番使いやすいと思うようになった。一気に飛んだな。つまり、C 言語によるオブジェクト記述法 COOL 4-2.再コンパイル不要インターフェイスのように。動的バインディング・インタフェースについては、私の理解範囲を超えましたので、よくわかりません。つーか、ifdefの嵐はどうも好きになれん。
それでもmallocやらfreeは処理コストが高いので、静的に確保した方が処理コストを抑えることができるみたいなんだけど、その静的に確保する際のサイズをどうやって決めたらいいか、よくわからないのが今のレベル。。。1024とか、4096とかって数字としてはキリがいいのがわかるんだけど、いつ1024を採用して、いつ4096を採用するなんてことが決められない。。。やっぱり動的の方がいいのよ。うーん。まだまだ先が長いなー。
えと、追記。
なぜ、staticが嫌かと言うと。こんなときに困ってしまうから。つーか、そもそもこんな使い方すんなってことかもしれん。。。
#include#include #include #define BUFLEN 16 char *getMyString(char *string, int length) { assert(BUFLEN > length); static char myString[BUFLEN]; strncpy(myString, string, length + 1); return myString; } int main() { char *hoge, *foo; hoge = getMyString("hogehoge", 8); foo = getMyString("foobar", 6); printf("hoge: %s\n", hoge); printf("foo: %s\n", foo); }
もちろん結果は。l
hoge: foobar foo: foobar
となるので、hogeの領域の文字列hogehogeがなくなってしまうからだ。まぁ、当然だけど、静的ローカル変数はリエントラントでないのが、嫌というわけ。まぁ、もっとちゃんとしたサンプルコードを書けば、誤解はなかったかも。
Shin Ohno 2003-2012