配列とポインタの違い(p87)
変数名とは
「変数名」とは、データの格納領域の先頭アドレスを、human-readableに置き換えたシンボル名を指します。IPアドレスとホスト名の関係ですね。シンボル名に対応する格納領域の先頭アドレスは、プログラム内では「&」演算子でアドレスを表示したり、nmコマンドのダンプ結果で確認できます。配列とポインタの違い
pointer.c#include <stdio.h> char s1[] = "This is array."; char *s2 = "This is pointer."; int main() { printf(" s1 = 0x%p\n", s1); printf("&s1 = 0x%p\n", &s1); printf(" s2 = 0x%p\n", s2); printf("&s2 = 0x%p\n", &s2); printf("sizeof(s1) = %d\n", sizeof(s1)); printf("sizeof(s2) = %d\n", sizeof(s2)); return 0; }
実行結果
s1 = 0x0x804a020 &s1 = 0x0x804a020 s2 = 0x0x8048560 &s2 = 0x0x804a030 sizeof(s1) = 15 sizeof(s2) = 4
配列名を右辺値として参照すると、その配列名(シンボル)に対応する格納領域の先頭アドレスに展開されます。これは、&演算子をつけたときに表示されるアドレスと同じです。そのため、s1==&s1となります。
一方、ポインタ変数を右辺値として参照すると、そのポインタ変数に格納されたデータ(ポインタ値)に展開されます。もちろんポインタ変数自身も、特定の格納領域の先頭アドレスに対応しているので、&演算子をつけると、対応するアドレスに展開されます。ポインタ変数の領域に格納された「ポインタ値」と、シンボルの「アドレス」は異なるものなので、s2!=&s2となります。
nmコマンドで、このプログラムのシンボルテーブルをダンプしてみると、以下のように、シンボルs1とs2に対応する領域の先頭アドレスを確認することができます(最後の2行)。
$ nm pointer 08049f14 d _DYNAMIC 0804a000 d _GLOBAL_OFFSET_TABLE_ 0804855c R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable w _Jv_RegisterClasses 080486c0 r __FRAME_END__ 08049f10 d __JCR_END__ 08049f10 d __JCR_LIST__ 0804a034 D __TMC_END__ 0804a034 B __bss_start 0804a018 D __data_start 080483d0 t __do_global_dtors_aux 08049f0c t __do_global_dtors_aux_fini_array_entry 0804a01c D __dso_handle 08049f08 t __frame_dummy_init_array_entry w __gmon_start__ 08049f0c t __init_array_end 08049f08 t __init_array_start 08048540 T __libc_csu_fini 080484d0 T __libc_csu_init U __libc_start_main@@GLIBC_2.0 08048350 T __x86.get_pc_thunk.bx 0804a034 D _edata 0804a038 B _end 08048544 T _fini 08048558 R _fp_hw 080482b4 T _init 08048320 T _start 0804a034 b completed.6339 0804a018 W data_start 08048360 t deregister_tm_clones 080483f0 t frame_dummy 0804841c T main U printf@@GLIBC_2.0 08048390 t register_tm_clones 0804a020 D s1 0804a030 D s2