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番地大(高)

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

関連

抽象。

  • 書いた順番と実際の配置の対応は、処理する側しだいで保証されない

関連。

Backlinks