プログラミング実習 III (クラス 4)

[B3] ポインタ

int 型変数がメモリ上にどのように配置されるか

int 型変数 a、 b を宣言し、 初期化するだけの単純なプログラム。

ex1.c:

 int a = 1234;
 int b = 6666;
 
 int
 main (void)
 {
   return 0;
 }

gcc でコンパイルし、 オブジェクトファイルを生成する。 コンパイラの最適化を抑止するため -O0 オプションをつける。

> gcc -O0 -c ex1.c

生成されたファイル ex1.o を確認する。 1152 バイトの EFL 形式のオブジェクトファイルであることがわかる。

> ls -l ex1.o 
-rw-r--r-- 1 ohsaki lsnl 1152 Oct 14 09:12 ex1.o
> file ex1.o 
ex1.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

バイナリダンプしてみる。 バイナリファイルであるため、 ASCII でダンプしてもよくわからない。

> xxd ex1.o | head -20
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0100 3e00 0100 0000 0000 0000 0000 0000  ..>.............
00000020: 0000 0000 0000 0000 c001 0000 0000 0000  ................
00000030: 0000 0000 4000 0000 0000 4000 0b00 0a00  ....@.....@.....
00000040: 5548 89e5 b800 0000 005d c300 d204 0000  UH.......]......
00000050: 0a1a 0000 0047 4343 3a20 2844 6562 6961  .....GCC: (Debia
00000060: 6e20 3134 2e32 2e30 2d31 3929 2031 342e  n 14.2.0-19) 14.
00000070: 322e 3000 0000 0000 1400 0000 0000 0000  2.0.............
00000080: 017a 5200 0178 1001 1b0c 0708 9001 0000  .zR..x..........
00000090: 1c00 0000 1c00 0000 0000 0000 0b00 0000  ................
000000a0: 0041 0e10 8602 430d 0646 0c07 0800 0000  .A....C..F......
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0100 0000 0400 f1ff  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0300 0100 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0700 0000 1100 0200  ................
00000100: 0000 0000 0000 0000 0400 0000 0000 0000  ................
00000110: 0900 0000 1100 0200 0400 0000 0000 0000  ................
00000120: 0400 0000 0000 0000 0b00 0000 1200 0100  ................
00000130: 0000 0000 0000 0000 0b00 0000 0000 0000  ................

ELF 形式のオブジェクトファイルなので、 readelf コマンドでどんなセクションがあるか見てみる。 .text がテキストセグメント (機械語の命令が書かれたセグメント)、 .data がデータセグメント (データが格納されたセグメント)。

 > readelf -S ex1.o
 There are 11 section headers, starting at offset 0x1c0:
 
 Section Headers:
   [Nr] Name              Type             Address           Offset
        Size              EntSize          Flags  Link  Info  Align
   [ 0]                   NULL             0000000000000000  00000000
        0000000000000000  0000000000000000           0     0     0
   [ 1] .text             PROGBITS         0000000000000000  00000040
        000000000000000b  0000000000000000  AX       0     0     1
   [ 2] .data             PROGBITS         0000000000000000  0000004c
        0000000000000008  0000000000000000  WA       0     0     4
   [ 3] .bss              NOBITS           0000000000000000  00000054
        0000000000000000  0000000000000000  WA       0     0     1
   [ 4] .comment          PROGBITS         0000000000000000  00000054
        0000000000000020  0000000000000001  MS       0     0     1
   [ 5] .note.GNU-stack   PROGBITS         0000000000000000  00000074
        0000000000000000  0000000000000000           0     0     1
   [ 6] .eh_frame         PROGBITS         0000000000000000  00000078
        0000000000000038  0000000000000000   A       0     0     8
   [ 7] .rela.eh_frame    RELA             0000000000000000  00000150
        0000000000000018  0000000000000018   I       8     6     8
   [ 8] .symtab           SYMTAB           0000000000000000  000000b0
        0000000000000090  0000000000000018           9     3     8
   [ 9] .strtab           STRTAB           0000000000000000  00000140
        0000000000000010  0000000000000000           0     0     1
   [10] .shstrtab         STRTAB           0000000000000000  00000168
        0000000000000054  0000000000000000           0     0     1
 Key to Flags:
   W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
   L (link order), O (extra OS processing required), G (group), T (TLS),
   C (compressed), x (unknown), o (OS specific), E (exclude),
   D (mbind), l (large), p (processor specific)

.data セグメントを逆アセンブラでダンプしてみる。

 > objdump -d -j .data ex1.o
 
 ex1.o:     file format elf64-x86-64
 
 
 Disassembly of section .data:
 
 0000000000000000 <a>:
    0:   d2 04 00 00                                         ....
 
 0000000000000004 <b>:
    4:   0a 1a 00 00                                         ....

変数 a が d2 04 00 00、 変数 b が 0a 1a 00 00 の 4 バイトにコンパイルされていることがわかる。

64 ビット OS 上の C コンパイラの int は通常 32 ビットなので、それぞれ 4 バイト。 使用している計算機の CPU は Intel Core Ultra 5 で、 1234 は 16 進数で 0x00001a0a である。 インテルの CPU はリトルエンディアンなので、下位バイトが前に来て、 d2 04 00 00 のように格納されている。

変数 a と変数 b がつめて配置されていることにも注意。


構造体がメモリ上にどのように配置されるか

構造体のサンプル。

ex2.c:

 typedef struct
 {
   char name[4];
   int price;
   double weight;
 } item_t;
 
 item_t a = { "APP", 120, 205.8 };
 
 int
 main (void)
 {
   return 0;
 }

コンパイルや確認の手順はさきほど (int 型のもの) と同じ。

 > gcc -O0 -c ex2.c
 > objdump -d -j .data ex2.o
 
 ex2.o:     file format elf64-x86-64
 
 
 Disassembly of section .data:
 
 0000000000000000 <a>:
    0:   41 50 50 00 78 00 00 00 9a 99 99 99 99 b9 69 40     APP.x.........i@

char が 4 バイト、int が 4 バイト、ダブル 8 バイトが連続したメモリ領域に確保されている。

"APP" は最後 (4 バイト目) に NULL が入っていることに注意。

120 は 16 進数で 0x00000078 であり、 205.8 は 16 進数で 9a 99 99 99 99 b9 69 40 の 8 バイトである。 120 は 32 ビットの符号なし整数が、 リトルエンディアンで格納されている。 205.8 は倍精度 (double precision) の IEEE 745 浮動小数点フォーマットで符号化されたバイト列が格納されている。

IEEE 754
https://en.wikipedia.org/wiki/IEEE_754

配列がメモリ上にどのように配置されるか

配列のサンプル。 配列の要素は int 型。

ex3.c:

 int x[] = { 0, 10, 20, 30, 40 };
 
 int
 main (void)
 {
   return 0;
 }

コンパイルや確認の手順はさきほど (int 型のもの) と同じ。

 > gcc -O0 -c ex2.c
 > objdump -d -j .data ex3.o
 
 ex3.o:     file format elf64-x86-64
 
 
 Disassembly of section .data:
 
 0000000000000000 <x>:
    0:   00 00 00 00 0a 00 00 00 14 00 00 00 1e 00 00 00     ................
   10:   28 00 00 00                                         (...

これも、 配列の要素である int 型の 0, 10, 20, 30, 40 が詰めて配置されていることがわかる。 したがって、n 番目の要素は、配列の先頭から 4 * (n - 1) バイト目にある。

C コンパイラは、 配列のデータをこのようにメモリ上に配置するので、 配列の n 番目の要素へのアクセスが瞬時に行える。 つまり、 n 番目の要素にアクセスしたければ、 配列の先頭から 4 * (n - 1) バイト目からの連続する 4 バイトにアクセスすればよい。



Hiroyuki Ohsaki (ohsaki[atmark]lsnl.jp)