PC 何时在 if 块内分配 static char*

When does PC allocate static char* inside of if block

如果我有

if (a)
{
    char a[10000]=AllElementsFromBufferA();
    DoSomethingWithA(a);
}
else if (b)
{
    char b[10000]=AllElementsFromBufferB();
    DoSOmethingWithB(b);
}
else if (c)
{
.
.
.
x 70;

PC什么时候分配这个?即使它不去这里也会分配它吗? 因为我有很多 if-s 和 else-s 的代码并且性能非常重要,所以我不知道我是否应该动态分配它或像这样。因为,如果这消耗了太多内存,那也很糟糕。

谢谢!

编辑 1 我举了一些更现实的例子

据我所知,编译器会在函数入口为所有块作用域变量分配 space,无论分支是否被采用,至少对于小变量(标量、小数组等) .在大多数平台上,搁置堆栈 space 只是调整堆栈指针的问题,这是一个相当便宜的操作。此外,我认为除了 space 之外,一些 ABI 要求您为本地人留出一些 "scratch" space。

我写了下面的例子:

#include <stdio.h>

int main( void )
{
  int foo = 0;
  scanf( "%d", &foo );

  if ( foo == 1 )
  {
    char a[10000] = "foo";
    printf( "a = %s\n", a );
  }
  else if ( foo == 2 )
  {
    char b[10000] = "bar";
    printf( "b = %s\n", b );
  }
  else
  {
    char c[10000] = "bletch";
    printf( "c = %s\n", c );
  }

  printf( "done\n" );

  return 0;
}

用gcc(5.3.0, MinGW)编译如下:

gcc -S alloc.c

这给了我以下机器代码清单:

        .file   "alloc.c"
        .def    ___main;        .scl    2;      .type   32;     .endef
        .section .rdata,"dr"
LC0:
        .ascii "%d[=12=]"
LC1:
        .ascii "a = %s[=12=]"
LC2:
        .ascii "b = %s[=12=]"
LC3:
        .ascii "c = %s[=12=]"
LC4:
        .ascii "done[=12=]"
        .text
        .globl  _main
        .def    _main;  .scl    2;      .type   32;     .endef
_main:
LFB10:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        andl    $-16, %esp
        movl    032, %eax              // allocate stack space
        call    ___chkstk_ms              // with these three
        subl    %eax, %esp                // instructions
        call    ___main
        movl    [=12=], 10028(%esp)
        leal    10028(%esp), %eax
        movl    %eax, 4(%esp)
        movl    $LC0, (%esp)
        call    _scanf
        movl    10028(%esp), %eax
        cmpl    , %eax                 // if ( foo == 1 )
        jne     L2
        movl    03014, 28(%esp)       // a[] = "foo"
        leal    32(%esp), %eax
        movl    96, %edx
        movl    %edx, 8(%esp)
        movl    [=12=], 4(%esp)
        movl    %eax, (%esp)
        call    _memset
        leal    28(%esp), %eax
        movl    %eax, 4(%esp)
        movl    $LC1, (%esp)
        call    _printf
        jmp     L3
L2:
        movl    10028(%esp), %eax
        cmpl    , %eax                 // else if ( foo == 2 )
        jne     L4
        movl    96034, 28(%esp)       // b[] = "bar"
        leal    32(%esp), %eax
        movl    96, %edx
        movl    %edx, 8(%esp)
        movl    [=12=], 4(%esp)
        movl    %eax, (%esp)
        call    _memset
        leal    28(%esp), %eax
        movl    %eax, 4(%esp)
        movl    $LC2, (%esp)
        call    _printf
        jmp     L3
L4:                                        // else
        movl    52803938, 28(%esp)      // c[] = "bletch"
        movl    723, 32(%esp)
        leal    36(%esp), %eax
        movl    92, %edx
        movl    %edx, 8(%esp)
        movl    [=12=], 4(%esp)
        movl    %eax, (%esp)
        call    _memset
        leal    28(%esp), %eax
        movl    %eax, 4(%esp)
        movl    $LC3, (%esp)
        call    _printf
L3:
        movl    $LC4, (%esp)
        call    _puts
        movl    [=12=], %eax
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
LFE10:
        .ident  "GCC: (GNU) 5.3.0"
        .def    _scanf; .scl    2;      .type   32;     .endef
        .def    _memset;        .scl    2;      .type   32;     .endef
        .def    _printf;        .scl    2;      .type   32;     .endef
        .def    _puts;  .scl    2;      .type   32;     .endef

所以,gcc 在这里做了一些相当聪明的事情。首先,它能够确定您在任何给定时间只有 abc 中的一个处于活动状态,因此它不会尝试分配 space对于所有三个。相反,它为一个实例分配了足够的 space,加上一些临时 space:

    movl    032, %eax
    call    ___chkstk_ms
    subl    %eax, %esp

我假设 __chkstk_ms 确定是否有足够的堆栈 space 可用于这么大的请求,如果没有则抛出异常。

它没有尝试初始化每个实例,除非采用了特定的分支:

movl    03014, 28(%esp) // a[] = "foo"

movl    96034, 28(%esp) // b[] = "bar"

movl    52803938, 28(%esp) // c[] = "bletch"
movl    723, 32(%esp)

gcc 没有为字符串文字 "foo""bar""bletch" 留出 space,而是使用 movwmovl 复制 2 字节和 4 字节整数的指令,这些整数的位模式与这些字符串的 ASCII 字符序列相对应。 IOW:

   7303014  = 0x006f6f66 == "[=15=]oof"
   7496034  = 0x00726162 == "[=15=]rab"
1952803938  = 0x74656c62 == "telb"
     26723  = 0x00006863 == "[=15=][=15=]hc"

请记住 x86 是小端字节序的,所以所有内容都是 "backwards"。

重要

这是 one 版本 one 编译器在 one 平台上的行为 - 有没有理由相信不同的编译器会以相同的方式运行。然而,我的经验让我认为大多数编译器会尝试在分配 space 方面变得聪明,尤其是对于非常大的对象。