KDOC 228: 変数を書いた順番とアドレスの関係はコンパイラによって異なる

この文書のステータス

  • 作成
    • 2024-08-25 貴島
  • レビュー
    • 2024-09-09 貴島

概要

コンパイラによって、コードで変数を初期化した順番とアドレスの順番は異なることがある。

最初に、gcc。

int main() {
  char base = 0;
  char a = 'a';
  char b = 'b';
  char c = 'c';

  printf("%p: base\n", &base);
  printf("%p: a\n", &a);
  printf("%p: b\n", &b);
  printf("%p: c\n", &c);
}
0x7fffa55f0a84: base
0x7fffa55f0a85: a
0x7fffa55f0a86: b
0x7fffa55f0a87: c

つまり、メモリは↓このようになっているように見える。コードで変数を初期化した順番とアドレスの順番が一致している。スタックだからアドレスが書いた逆順になる、というわけではない。

^番地小(低)

| ...84: base |
| ...85: a |
| ...86: b |
| ...87: c |

v番地大(高)

アセンブラを見てみると、(低)base, a, b, c(高)順のアドレスになっているのを確認できた。

        .file   "main.c"
        .text
        .section        .rodata
.LC0:
        .string "%p: base\n"
.LC1:
        .string "%p: a\n"
.LC2:
        .string "%p: b\n"
.LC3:
        .string "%p: c\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    %fs:40, %rax
        movq    %rax, -8(%rbp)
        xorl    %eax, %eax
        movb    $0, -12(%rbp)  ; 👈
        movb    $97, -11(%rbp) ; 👈
        movb    $98, -10(%rbp) ; 👈
        movb    $99, -9(%rbp)  ; 👈
        leaq    -12(%rbp), %rax
        movq    %rax, %rsi
        leaq    .LC0(%rip), %rax
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf@PLT
        leaq    -11(%rbp), %rax
        movq    %rax, %rsi
        leaq    .LC1(%rip), %rax
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf@PLT
        leaq    -10(%rbp), %rax
        movq    %rax, %rsi
        leaq    .LC2(%rip), %rax
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf@PLT
        leaq    -9(%rbp), %rax
        movq    %rax, %rsi
        leaq    .LC3(%rip), %rax
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf@PLT
        movl    $0, %eax
        movq    -8(%rbp), %rdx
        subq    %fs:40, %rdx
        je      .L3
        call    __stack_chk_fail@PLT
.L3:
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long   1f - 0f
        .long   4f - 1f
        .long   5
0:
        .string "GNU"
1:
        .align 8
        .long   0xc0000002
        .long   3f - 2f
2:
        .long   0x3
3:
        .align 8
4:

  --------------------------------
  |-12(%rbp): base |
  |-11(%rbp): a |
  |-10(%rbp): b |
  | -9(%rbp): c |

いっぽうclangでコンパイルした場合、先に書いた変数が高位アドレスとなる。

0x7ffe71de7317: base
0x7ffe71de7316: a
0x7ffe71de7315: b
0x7ffe71de7314: c

--------------------
^番地小(低)

| ...14: c |
| ...15: b |
| ...16: a |
| ...17: base |

v番地大(高)

つまり、コードで変数を初期化した順番と、アドレスの順番は必ずしも逆にはならないことがある。なぜそうなるのかは、いつか調べる。

関連