void fun2(char *format, ...){
va_list arg_list;
va_start(arg_list, format);
vprintf(format, arg_list);
va_end(arg_list);
}
void fun1(char *format, ...){
fun2(format);
}
int main(){
fun1("test: %d", 100);
}
输出:
test: 100
https://onlinegdb.com/OfdDeSJg_
上面的例子有没有错误或者不推荐的地方?
我猜想当调用 fun2(format);
时,只传递了指向第一个参数 (format
) 的指针,对吗?
当fun2
中的vprintf
访问整数100时,这个整数在哪里?在为fun1
保留的堆栈中,在为fun2
保留的堆栈中,在为vprintf
保留的堆栈中,还是在其他地方?
如果像我想象的那样只有指向第一个参数的指针被传递给fun2
,这是否意味着当fun2
调用的函数和宏访问整数100时它们正在访问为 fun1
保留的堆栈?
最佳答案
它们不会被转发。此代码不起作用,除非你很幸运。
如果你幸运的话,那可能是因为优化器看到 format
保持在相同的参数位置(寄存器或堆栈),所以 fun1 实际上没有其中的任何“真实”代码(它不会移动任何参数),因此它将 fun1 更改为直接跳转到 fun2 的跳转指令(也称为尾调用优化)。然后因为 fun1 没有弄乱任何参数也没有创建堆栈帧,它们仍然都在调用 void (char*, ...)
函数的正确位置,fun1 在那里接他们。
即汇编代码大概是这样的:
// Pretend calling convention: format is in ecx, and varargs on the stack
fun2:
// actual code would go here
ret
fun1:
// jmp instruction doesn't affect the stack, nor ecx
// so fun2 receives exactly the same inputs as if main
// had directly called fun2
jmp fun2
main:
mov ecx, "test: %d"
push 100
call fun1
ret
你不能依赖它。如果您想可靠地转发可变参数,您可以更改您的函数以使用 va_list 参数(如 vprintf),或者您编写自己的汇编代码。
https://stackoverflow.com/questions/72966417/