c - 可变参数在多次转发时如何在堆栈上表示?

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/

相关文章:

r - 如何用 NA、na_if、if_else、regex 替换某些字符串

api - 概念 api 缺少属性值

c - 这两个初始化是等价的吗?

html - 在响应图像上放置和缩放文本

javascript - 尝试在 Mobile Safari 上调试 Javascript 但当 i

javascript - 我怎样才能从传递给 `String.prototype.replace`

python - 在具有列表作为值的 python 字典中查找最低值或最少的项目

perl - 如何为每个 XML 输出添加换行符?

r - 在行内的顺序中调整很少的频率来计算频率

regex - 如何从文件中提取一行中存在的所有 IP 地址?