day4_ポインタと文字列リテラル
配列として指定する文字列リテラルと、ポインタとして指定する文字列リテラルの違いについて
int func_pntstr() {
char str[] = "LIST";
char* strP = "POINTER";
printf("%s\n", str); //LIST
printf("%s\n", strP); //POINTER
return 0;
}
正直やってることはおなじに見えるし、どっちでも良くね?とも思ったんですが、色々調べてみると異なるようだ。 本日の教材として相変わらずこちらにサイトを利用させていただいています。
違いとしては
・配列として利用する場合は、メモリ上に元々存在している文字列分のサイズを新たにメモリとして確保し、文字列を割り当てる。
・ポインタとして指定する場合は、ポインタのアドレスがそのメモリ上に元々存在している文字列のアドレス自体を指し示す。
とのこと。 配列で割り当てる場合、割り当てた文字を変更する場合は1文字ずつ書き換えるか、strcpy()といった関数で書き換える必要があるが ポインタ変数の場合は、新たに別の文字列をそのまま割り当てることが出来るらしい。
一方、文字列リテラルそのものを書き換えることはC言語では許されないらしく、ポインタ変数で割り当てられた文字列を1文字変更する、といったことはできないようだ。
なんとなく言葉は理解出来たがいまいちピンと来なかったので、例によってVisualStudioのアセンブリで確認してみる。 全部乗せると見にくいので必要なところだけピックアップ。
30: char str[] = "LIST";
00D85092 A1 DC 7B D8 00 mov eax,dword ptr [string "LIST" (0D87BDCh)]
00D85097 89 45 F0 mov dword ptr [str],eax
00D8509A 8A 0D E0 7B D8 00 mov cl,byte ptr ds:[0D87BE0h]
00D850A0 88 4D F4 mov byte ptr [ebp-0Ch],cl
31:
32: char* strP = "POINTER";
00D850A3 C7 45 E4 E4 7B D8 00 mov dword ptr [strP],offset string "POINTER" (0D87BE4h)
33:
34: printf("%s\n", str); //LIST
00D850AA 8D 45 F0 lea eax,[str]
00D850AD 50 push eax
00D850AE 68 D0 7C D8 00 push offset string "%s\n" (0D87CD0h)
00D850B3 E8 8E BF FF FF call _printf (0D81046h)
00D850B8 83 C4 08 add esp,8
35: printf("%s\n", strP); //POINTER
00D850BB 8B 45 E4 mov eax,dword ptr [strP]
00D850BE 50 push eax
00D850BF 68 D0 7C D8 00 push offset string "%s\n" (0D87CD0h)
00D850C4 E8 7D BF FF FF call _printf (0D81046h)
00D850C9 83 C4 08 add esp,8
なるほど、たしかに文字列リテラル"LIST"の場合、レジスタ経由で文字列自体をコピーして利用しているのに対し 文字列リテラル"POINTER"の場合は、文字列のアドレスをそのまま取得しているように思える。
おそらく文字列リテラルはPEファイルの変更不可能(Read Only)の領域に確保されており 一度コピーして新たに確保された領域内ではいじることはできるが、そのままの領域では変更ができない、ということなんだろう。
新たに別の文字列リテラルを割り当てることができる、というのはコンパイル時に新たに割り当てる文字列も変更不可能領域に保管されるため ポインタのアドレスを変更するだけで文字列の変更が実現可能になる、という解釈をしました。
day3_ポインタ変数の初期化
勉強しては触るのサボって忘れて、勉強しては触るの忘れてを繰り返すポインタ
ポインタ変数ってなんか初期化のときとそれ以外でなんか違う動きしない? というのが毎回混乱の原因だと思ったので、今回はそれを文字にしておきたい。
続きを読むday1_HelloWorldから学んだこと
int func1() { char strings[] = "Hello World"; printf("%s\n", strings); return 0; }続きを読む