文字列から特定の文字を削除する単純な関数をコーディングしているときに、私はこの奇妙な問題に陥りました。
void str_remove_chars( char *str, char to_remove)
{
if(str && to_remove)
{
char *ptr = str;
char *cur = str;
while(*ptr != '\0')
{
if(*ptr != to_remove)
{
if(ptr != cur)
{
cur[0] = ptr[0];
}
cur++;
}
ptr++;
}
cur[0] = '\0';
}
}
int main()
{
setbuf(stdout, NULL);
{
char test[] = "string test"; // stack allocation?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // works
printf("After: %s\n",test);
}
{
char *test = "string test"; // non-writable?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // crash!!
printf("After: %s\n",test);
}
return 0;
}
2番目のテストが失敗するのはなぜですか。私には、最初の表記char *ptr = "string";
は次の表記と同じように見えます:char ptr[] = "string";
。
そうではありませんか?
2つの宣言は同じではありません。
char ptr[] = "string";
はサイズ7
のchar配列を宣言し、文字で初期化しますs
、t
、r
、i
、n
、g
および\0
。あなたは許可この配列の内容を変更することができます。
char *ptr = "string";
はptr
をcharポインターとして宣言し、string literalのアドレスで初期化します"string"
は読み取り専用。文字列リテラルの変更は未定義の動作です。あなたが見たもの(seg fault)は未定義の振る舞いの一つの現れです。
厳密に言えば、char *ptr
の宣言は、文字型へのポインタを保証するだけです。文字列が、一部のオペレーティングシステムによって読み取り専用に設定されるコンパイル済みアプリケーションのコードセグメントの一部を形成することは珍しいことではありません。問題は、事前に定義された文字列(書き込み可能であること)の性質について、実際には自分でその文字列のメモリを明示的に作成したことがない場合に、想定していることです。コンパイラとオペレーティングシステムの実装によっては、これまでに実行しようとしたことを実行できる場合があります。
一方、char test[]
の宣言は、定義により、実際には、この場合、スタック上の文字の配列全体に対して読み書き可能なメモリを割り当てます。
char *test = "string test";
は間違っています。const char*
である必要があります。このコードは、下位互換性の理由でコンパイルされます。 const char*
が指すメモリは読み取り専用メモリであり、メモリに書き込もうとすると、未定義の動作が発生します。一方、char test[] = "string test"
はスタックにwritable文字配列を作成します。これは、書き込み可能な他の通常のローカル変数と同様です。
覚えてる限り
char ptr[] = "string";
スタックに"string"
の-copyを作成するため、これは変更可能です。
フォーム
char *ptr = "string";
の下位互換性です
const char *ptr = "string";
また、(未定義の動作に関して)コンテンツを変更することはできません。コンパイラは、このような文字列をメモリの読み取り専用セクションに配置する場合があります。
char *str = strdup("test");
str[0] = 'r';
適切なコードであり、変更可能な文字列を作成します。 strにはヒープ内のメモリが割り当てられ、値「test」が入力されます。
良い答え@codaddict。
また、sizeof(ptr)
は、宣言ごとに異なる結果を返します。
最初の配列宣言は、配列の長さを返します 含む 終了ヌル文字。
2つ目はchar* ptr = "a long text...";
は、ポインターの長さ(通常は4または8)を返します。