私は今日Cに関する本を読んでいて、次のことが真実であると述べました。なぜ私がこのプログラムを検証のために作ったのか、私はとても興味がありました。そして、最終的にここに投稿して、私より賢い人が、これら2つのケースが実行時に異なる理由を教えてくれるようにします。
(char *)がリテラルとして作成された文字列を指しているのか、mallocおよび手動入力で作成された文字列を指しているのかに基づいて、実行時に(char *)が処理される方法の違いに関連する質問の詳細。
なぜこのようにメモリによって割り当てられたメモリがより保護されるのですか?また、答えは「バスエラー」の意味を説明していますか?
これは私が書いたプログラムで、ユーザーにクラッシュするかどうかを尋ね、プログラムが正常にコンパイルされることを示しています。そして、私の頭の中では、両方のオプションのコードが概念的に同一であることを強調します。しかし、それが私がここにいる理由です。なぜそうではないのかを理解するためです。
// demonstrate the difference between initializing a (char *)
// with a literal, vs malloc
// and the mutability of the contents thereafter
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int main() {
char cause_crash;
char *myString;
printf("Cause crash? ");
scanf("%c", &cause_crash);
if(cause_crash == 'y') {
myString = "ab";
printf("%s\n", myString); // ab
*myString = 'x'; // CRASH!
printf("%s\n", myString);
} else {
myString = malloc(3 * sizeof(char));
myString[0] = 'a';
myString[1] = 'b';
myString[2] = '\0';
printf("%s\n", myString); // ab
*myString = 'x';
printf("%s\n", myString); // xb
}
return 0;
}
編集:結論
以下にいくつかの良い答えがありますが、ここで私が理解するようになったものを簡潔に要約したいと思います。
基本的な答えはこれのようです:
コンパイラが「文字列リテラル」が(char *)変数に割り当てられていることを確認すると、ポインタは静的なメモリを指します(おそらく実際にはバイナリの一部ですが、通常は、より低いレベルのシステムによって読み取り専用として強制されます。つまり、メモリはプログラムのその部分で動的に割り当てられるのではなく、リテラルの内容を格納する静的メモリの領域を指すようにポインタが設定されるだけです。
この解決策について私が指摘したいことがいくつかあります。
1.最適化が動機となる可能性があります。私のコンパイラでは、同じ文字列リテラルで初期化された2つの異なる(char *)変数が実際には同じアドレスを指しています。
char *myString = "hello";
char *mySecond = "hello"; // the pointers are identical! This is a cool optimization.
2興味深いことに、変数が実際に((char *)ではなく)charの配列である場合、この(#1)は真ではありません。これは私にとって興味深いものでした。なぜなら、(コンパイル後の)配列はcharsへのポインターと同一であるという印象を受けていたからです。
char myArString[] = "hello";
char myArSecond[] = "hello"; // the pointers are NOT the same
3いくつかの答えが示唆したことを要約すると、新しいメモリを割り当てchar *myString = "Hello, World!"
ず、すでに存在するメモリを指すようにmyStringを設定するだけです。おそらくバイナリで、おそらくメモリの特別な読み取り専用ブロックで...など。
4テストを通じて、新しいメモリを割り当てることchar myString[] = "Hello, World!"
がわかりました。私が知っているのは、この方法で作成した場合、文字列は変更可能であるということです。