我有这个...奇怪的问题,我正在拼命寻找解决方案。
我们有这个例子:(为32位系统制作)
#include <stdio.h>
//unsigned foo(unsigned arg_a, unsigned arg_b) {
unsigned foo(unsigned arg_a, ...) {
unsigned *temp = (unsigned *)((unsigned)&arg_a + 4);
return *temp + arg_a;
}
int main(void) {
int i = foo(0xbe00, 0x00af);
printf("We got a %x\n", i);
return 0;
}
函数foo有2个参数。目标是根据arg_a的地址“猜测”arg_b的地址。这是基于调用者(main)将arg_a和arg_b推入堆栈的假设(所以(int)&amp; arg_b - (int)&amp; arg_a == 4)。
根据编译器和优化级别,输出的不同之处如下:
gcc -g -Wall -O0 test.c
./a.out
We got a beaf
gcc -g -Wall -O1 test.c
./a.out
We got a beaf
gcc -g -Wall -O2 test.c
./a.out
We got a 80542b0
gcc -g -Wall -O3 test.c
./a.out
We got a 80542b0
clang -g -Wall -O0 test.c
./a.out
We got a 8054281
clang -g -Wall -O1 test.c
./a.out
We got a 805423f
clang -g -Wall -O2 test.c
./a.out
We got a b768d9d6
clang -g -Wall -O3 test.c
./a.out
We got a b76899d6
(这个例子很不稳定,例如把一个printf放在foo里面,gcc总是打印出“我们得到了一个beaf”.Clang .. not ..)
上面的例子只是我尝试清楚的方法。
我的真实问题(和我的目标)如下: 如何从foo函数内部使用CLANG提取arg_a的原始地址,即调用者(主)在堆栈中推送0xbe00的地址? (gcc不是一个选项,虽然它仍然很有趣)
Thanx你的时间!
编辑:制作foo可变参数以使问题更有意义......
答案 0 :(得分:4)
简而言之,不要这样做。
您正在做的是未定义的行为。如果您想要arg_b
的地址,请写下&arg_b
。由于C的别名规则,试图通过使用arg_a
的指针欺骗来实现它。编译器假定某些指针不会相互别名,因此优化程序会在您执行此操作时进行不再适用的假设。结果,代码功能不正常。
答案 1 :(得分:0)
我不认为这对你有用。
首先,如果gcc是你的编译器,为什么不直接引入标准C库?如果您不想这样做,那么从标准实现中提取stdarg.h呢?
我正在看的stdarg.h的版本(在Windows上附带gcc 4.4.0)基本上是一堆调用编译器内置的宏 - 如果你有正确的编译器,那么应该很容易,即使你没有使用标准库的其余部分(我可以理解不愿意把整个混乱局面拉下来)。
如果这对您不起作用,那么尝试将您正在编写的函数标记为可变参数,并在编译器文档中查看它如何构建堆栈。您可能必须根据平台编写一些汇编程序片段,但通过将函数声明为可变参数,您将强制编译器以可预测的方式构建堆栈。
如果您的编译器不支持函数的可变参数,那么您可能需要重新考虑其他选项。
答案 2 :(得分:0)
您正在做的事情导致未定义的行为。
访问可选参数(来自...
)的正确方法是使用{{1}中的va_list
,va_start()
,va_arg()
和va_end()
宏}}
使用这些宏的示例很多,您可以轻松地在线找到它们。
这里不允许猜测。