我的C代码片段获取参数的地址并将其存储在易失性存储器位置(预处理代码):
void foo(unsigned int x) { *(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)(&x); } int main() { foo(1); while(1); }
我使用SVN版本的GCC来编译这段代码.在函数结束时,foo
我希望将值1
存储在堆栈中,并且在0x40000d4
指向该值的地址处.当我使用标志进行编译时没有进行优化时-O0
,我得到了预期的ARM7TMDI程序集输出(为方便起见而注释):
.align 2 .global foo .type foo, %function foo: @ Function supports interworking. @ args = 0, pretend = 0, frame = 8 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. sub sp, sp, #8 str r0, [sp, #4] @ 3. Store the argument on the stack mov r3, #67108864 add r3, r3, #212 add r2, sp, #4 @ 4. Address of the stack variable str r2, [r3, #0] @ 5. Store the address at 0x40000d4 add sp, sp, #8 bx lr .size foo, .-foo .align 2 .global main .type main, %function main: @ Function supports interworking. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 stmfd sp!, {r4, lr} mov r0, #1 @ 1. Pass the argument in register 0 bl foo @ 2. Call function foo .L4: b .L4 .size main, .-main .ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
它清楚地将参数存储在堆栈中,并从那里存储它0x40000d4
.当我使用优化进行编译时-O1
,我得到了一些意想不到的东西:
.align 2 .global foo .type foo, %function foo: @ Function supports interworking. @ args = 0, pretend = 0, frame = 8 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. sub sp, sp, #8 mov r2, #67108864 add r3, sp, #4 @ 3. Address of *something* on the stack str r3, [r2, #212] @ 4. Store the address at 0x40000d4 add sp, sp, #8 bx lr .size foo, .-foo .align 2 .global main .type main, %function main: @ Function supports interworking. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 stmfd sp!, {r4, lr} mov r0, #1 @ 1. Pass the argument in register 0 bl foo @ 2. Call function foo .L4: b .L4 .size main, .-main .ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
这次参数永远不会存储在堆栈中,即使堆栈中的某些内容仍然存储在堆栈中0x40000d4
.
这只是预期/未定义的行为吗?我做错了什么或者我实际上发现了Compiler Bug™?
一旦你返回foo()
,x
就会消失,任何指针都无效.随后使用这样的指针会导致C标准喜欢称之为"未定义的行为",这意味着绝对允许编译器假定您不会取消引用它,或者(如果您坚持要这样做)无需生成代码像你期望的那样远程做任何事情.如果你希望指针x
在foo()
返回后保持有效,你就不能x
在foo的堆栈上分配期限 - 即使你原则上知道没有任何理由可以破坏它 - 因为在C中不允许这样做,无论多久发生一次你的期望.
最简单的解决方案可能是x
在main()
(或在任何其他函数中具有足够长寿的范围)中创建局部变量并将地址传递给foo.您还可以创建x
一个全局变量,或者使用它在堆上分配它malloc()
,或者以更奇怪的方式为它预留内存.您甚至可以尝试找出堆栈顶部的某些(希望)更便携的方式,并明确地将数据存储在堆栈的某些部分,如果您确定不需要其他任何东西而且您我确信这是你真正需要做的.但是,正如您所发现的那样,您一直使用的方法并不够可靠.