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 在这里做了一些相当聪明的事情。首先,它能够确定您在任何给定时间只有 a
或 b
或 c
中的一个处于活动状态,因此它不会尝试分配 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,而是使用 movw
和 movl
复制 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 方面变得聪明,尤其是对于非常大的对象。
如果我有
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 在这里做了一些相当聪明的事情。首先,它能够确定您在任何给定时间只有 a
或 b
或 c
中的一个处于活动状态,因此它不会尝试分配 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,而是使用 movw
和 movl
复制 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 方面变得聪明,尤其是对于非常大的对象。