这是一个简单的C代码
#include <stdio.h>
int a = 5;
static int b = 20;
int main(){
int c = 30;
return 0;
}
编译后没有优化:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 13
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
xorl %eax, %eax
movl $0, -4(%rbp)
movl $30, -8(%rbp)
popq %rbp
retq
.cfi_endproc
## -- End function
.section __DATA,__data
.globl _a ## @a
.p2align 2
_a:
.long 5 ## 0x5
我的问题是,static int b = 20;
在上面的程序集中在哪里?
我知道它们应该在内存的全局区域中,但是在编译版本中找不到。
答案 0 :(得分:7)
您的代码不使用b
,并且它是文件作用域的,因此其他文件中没有任何内容可以使用它。 GCC不会为它发出定义。
要回答标题问题:
带有非零初始值设定项的非const
静态/全局变量(即静态存储类)变量将进入.section .data
,而不是.bss
(零初始可变),或者.rdata
(Windows)/ .rodata
(Linux)处理非零的只读数据。
gcc没有完全可以自动转换为asm的完全死脑模式。请参见Disable all optimization options in GCC-GCC始终必须通过其内部表示进行转换。
即使在-O0
,GCC也会进行一次通行证,以排除未使用的东西。 可能有一种禁用该功能的方法,这与gcc甚至在-O0
所做的其他转换不同。
gcc和clang -O0
将每个语句编译到一个单独的asm块中,该块存储/重新加载所有内容(for consistent debugging),但是gcc仍在其块中应用其标准转换,例如(x+y) < x
对于有符号的x和y with gcc8 and newer或y<0
变为x / 10
,变成高半部分的乘+移。 (Why does GCC use multiplication by a strange number in implementing integer division?)。
并且if(false)
内的代码甚至在-O0
处也被gcc删除,因此您无法在GDB中jump
对其进行编码。
有些人关心调试版本的运行时性能,尤其是实时软件(如游戏或操作系统)的开发人员,如果运行速度太慢,则无法正确测试。 (游戏中的人机交互,或操作系统中的设备驱动程序。)
其他一些编译器在-O0
上比较死脑筋,因此您经常会看到看起来更像源表达式的asm。我想我已经看到没有优化的MSVC发出的指令将mov
立即插入寄存器,然后cmp reg,imm
,即在运行时执行仅依赖于立即数的分支,因此可以微不足道地进行计算在该表达式的编译时。
当然,确实有一些非优化的编译器,其整个目标只是使用固定模式进行音译。例如,我认为Tiny C Compiler几乎是一遍遍的,并且在运行过程中会发出asm(或机器代码)。参见Tiny C Compiler's generated code emits extra (unnecessary?) NOPs and JMPs展示了它的简单性:它总是在函数序言中发出sub esp, imm32
,并且只有在知道函数需要多少堆栈时才返回以填充函数末尾。 。即使答案为零,也无法删除它并收紧代码。
无论如何,查看优化的asm通常更有趣。编写带有args并返回值的函数,因此您可以看到asm有趣的部分,而不会产生很多样板内容和存储/重载噪声。 How to remove "noise" from GCC/clang assembly output?
答案 1 :(得分:3)
如果编译器未优化static
变量,它将进入进程的默认数据部分。
在汇编中,通常可以由程序员在指定用于描述数据部分的文件部分中进行控制。
C标准在第6.2.4节第3段中说:
一个其标识符声明为...的对象,其中存储类说明符为static,具有静态存储持续时间。它的生命周期是程序的整个执行过程,并且在程序启动之前,其存储值仅初始化一次。
使用以下代码:
static int a = 100;
int foo()
{
return (a / 2);
}
答案 2 :(得分:0)
整个问题有点不准确...(重读它,您实际上对“在上面的程序集中”很具体。。。哦,那么答案是“无处” ..而我剩下的答案是尚未发布的问题,但希望能解释为什么“无处”是您的问题的答案。
您有C源代码,然后将某些程序集显示为编译器输出(但未指定编译器),然后询问有关程序集的信息。
正在“ C抽象机”上定义C,而您正在查看此类抽象机的特定x86-64 实现。
虽然实现有有一些通常会导致静态变量终止的规则,但它完全取决于编译器-它希望如何实现它们。
在纯汇编中(如手写体或从CPU角度看),没有“静态值”之类的东西。您只有寄存器,内存和外围设备。
因此,在汇编语言(机器代码)中,您可以将某些寄存器或内存的某些部分用作静态变量。无论哪种方法都更适合您的需求(没有硬性规定会迫使您以任何特定方式进行操作,除非您必须在目标CPU的有效机器代码中表达您的想法,但这通常意味着数十亿种可能性,甚至当将自己限制为仅“合理的”方法,它比仅仅单一方法更倾向于数十种可能的方法。
您甚至可以(在x86-64中)创建一个有点复杂的方案,如何将值保持为代码状态(“内存的一部分”就是机器代码占用的内存),即,它不会被直接写入在内存中作为一个值,但是代码将遵循某些代码路径(从许多可能的路径)以获得正确的最终结果,即在代码本身中对值进行编码。例如,有一个图灵完备的方法,如何仅使用mov
指令将C源代码编译为x86-64机器代码,该指令可能不使用内存作为静态变量(不确定,是否添加了.data
部分或者也可以通过将其编译成mov
代码来避免它,但是从其绝对存在的意义上,应该可以很清楚地知道如何从理论上避免.data
。)
所以您要问的是具有特定编译时间选项的特定C编译器如何实现静态值(并且可能有一些变体,具体取决于所使用的源和选项)...
...或者如果您真正询问的是“程序集中的静态值存储在哪里”,那么答案是“只要您的机器代码有效且正确,就可以在任何地方” ,因为整个“静态值”概念的层次要高于CPU的操作水平,所以就好像对特定机器代码用途的解释“静态值”一样,但是CPU中没有特定的指令/支持来处理该问题。
答案 3 :(得分:-3)
静态变量未存储在内存中。它们只会在使用时出现 例如
static int b = 20; c = c + b;
将编译
添加c,“ 20”