4

C文字列宣言のいくつかの基本について混乱しています。次のコードを試してみたところ、いくつかの違いに気づきました。

char* foo(){
    char* str1="string";
    char str2[7]="string";
    char* str3=(char)malloc(sizeof(char)*7);
    return str1;
    /* OR: return str2; */
    /* OR: return str3; */
    }
void main()  {
    printf("%s",foo());
    return 0;
    }

foo()にstr1 / 2/3を一度に1つずつ返させ、結果をメインに出力しようとしました。str2は奇妙なものを返しましたが、str1とstr3は実際の「文字列」を返しました。

1.さて、3つの宣言の違いは何ですか?str2が機能しなかった理由は、ローカル変数として宣言されているからだと思いますが、それは正しいですか?

2.では、str1はどうですか?foo()が終了した後も結果が残る場合、それはメモリリークを引き起こしませんか?

3. Cで文字列を返す関数を作成し、その関数によって返された値を他のものに使用しようとしています。上記のどのstr宣言を使用する必要がありますか?

前もって感謝します!

4

3 に答える 3

9
char* str1="string";

これはstr1ポインタになります。文字列リテラルの最初の文字を指します。const文字列リテラルを変更することは許可されていないため、次のように定義する必要があります。

const char *str1 = "string";

..。

char str2[7]="string";

これにより、(ポインタstr2ではなく)の配列がchar作成され、文字列リテラルの内容がその配列にコピーされます。const;として定義する必要はありません。配列自体は書き込み可能です。サイズを省略して、初期化子によって決定されるようにすることもできます。

char str2[] = "string";

次に(終了の場合はプラス1の場合はsizeof str2 == 76バイト)。"string"'\0'

これ:

char* str3=(char)malloc(sizeof(char)*7);

正しく書かれておらず、コンパイルすらすべきではありません。少なくとも、コンパイラから警告を受け取る必要があります。malloc()の結果をタイプにキャストしていますchar。あなたはそれをに変換する必要がありますchar*

char *str3 = (char*)malloc(sizeof(char) * 7);

ただし、キャストは不要であり、場合によってはエラーをマスクできます。comp.lang.c FAQの質問7.7以降を参照してください:

char *str3 = malloc(sizeof(char) * 7);

しかしsizeof(char)、定義上は1なので、次のように書くことができます。

char *str3 = malloc(7);

malloc()メモリを割り当てますが、初期化されないため、をstr3指す文字列を出力しようとすると、ガベージが発生します。割り当てられたスペースに終了ヌルが含まれていない場合は、実行時のクラッシュが発生します。文字'\0'strcpy()たとえば、次のように初期化できます。

char *str3 = malloc(7);
if (str3 == NULL) {
    fprintf(stderr, "malloc failed\n");
    exit(EXIT_FAILURE);
}
strcpy(str3, "string");

コピーするデータが割り当てられたスペースより大きくならないように十分に注意する必要があります。(いいえ、`strncpy()はこの問題の答えではありません。)

void main()間違っている; する必要がありますint main(void)。教科書で使用するように指示された場合void main()は、より良い教科書を見つけてください。その作者はCをよく知りません。

また、使用しているライブラリ関数に適切な#includeディレクティブが必要です:<stdio.h>for printf()<stdlib.h>for exit()malloc()および<string.h>for strcpy()。各関数のドキュメントには、含めるヘッダーが記載されているはずです。

私はこれが吸収することがたくさんあることを知っています。すぐにそれをすべて理解することを期待しないでください。

comp.lang.cのFAQについて触れました; これは優れたリソースであり、特にセクション6では、配列とポインター、およびそれらの間のしばしば混乱する関係について説明しています。

質問3については、C関数から文字列を返す方法は、Cがメモリ割り当てを行う方法(基本的には自分で管理する必要があります)のため、驚くほど複雑であることがわかります。ローカル変数へのポインターを安全に返すことはできません。関数が戻ると変数が存在しなくなり、呼び出し元にダングリングポインターが残るため、を返すのstr2は危険です。文字列リテラルを返すことは問題ありません。これは、プログラムの実行全体に存在する匿名配列に対応しているためです。で配列を宣言し、その配列staticへのポインタを返すことができます。または、を使用することもできますmalloc()(これは最も柔軟なアプローチですが、呼び出し元は次のことを行う必要があります。free()メモリ)、または関数が結果をコピーするバッファへのポインタを呼び出すように呼び出し元に要求することができます。

一部の言語では、文字列値を作成して、関数から返すだけです。あなたが今発見しているように、Cはそれらの言語の1つではありません。

于 2013-01-22T20:34:08.013 に答える
6
char* str1="string";

.dataこれにより、または.textセグメントのいずれかに配置され、いつでもアクセスできるリテラル文字列へのポインタが作成されます。そのようなことをするときは、必ず宣言してconstください。変更しようとすると、厄介なことが起こる可能性があります。

char str2[7]="string";

これにより、リテラル文字列のコピーを使用してスタックにローカルバッファが作成されます。関数が戻ると使用できなくなります。それはあなたが得ている奇妙な結果を説明しています。

char* str3=(char)malloc(sizeof(char)*7);

これにより、ヒープ(初期化されていない)にバッファが作成され、解放するまで使用できます。そして、それを解放する必要があります。そうしないと、メモリリークが発生します。

于 2013-01-22T20:17:03.620 に答える
0

文字列リテラル "string"は、静的エクステントを持つ7要素の配列として格納されます。charつまり、そのメモリはプログラムの起動時に割り当てられ、プログラムが終了するまで保持されます。

宣言

char *str1 = "string";

文字列リテラルのアドレスをに割り当てますstr1str1関数が終了すると変数は存在しなくなりますが、その(リテラルのアドレス"string")は関数の外部でも有効です。

宣言

char str2[7] = "string";

str2の配列として宣言し、文字列リテラルの内容charをそれにコピーします。関数が終了すると、存在しなくなり、その内容は意味がなくなります。 str2

宣言

char *str3 = (char*) malloc(sizeof(char) * 7);

これは次のように簡略化できます

char *str3 = malloc(sizeof *str3 * 7);

初期化されていない7バイトのメモリブロックを割り当て、そのアドレスをにコピ​​ーしますstr3。関数が終了すると、変数str3は存在しなくなりますが、それが指すメモリは引き続き割り当てられます。記述されているように、呼び出し元のコードでポインタの値を保持しないため、これはメモリリークです。このブロックには何もコピーしないため、の出力はmainランダムになることに注意してください。

また、コンパイラのドキュメントに関数の正当な署名として明示的に記載されていない限り、代わりに使用してください。 void main()mainint main(void)

于 2013-01-22T20:27:43.400 に答える