【发布时间】:2026-01-15 13:25:02
【问题描述】:
我试图使用此代码实现我自己的可变参数函数。相反,我得到了 UB。
#include <stdio.h>
void test(int a, ...)
{
char* arg_a = (char*)&a;
char* arg_b = arg_a + sizeof(int);
printf("%c", *arg_b);
}
int main(){
test(1, 'a');
}
那么为什么这个程序不打印字母a?难道不期望参数1(test())将被写入函数堆栈帧中的低地址例如:0000 0004(因为0000 0000将保留用于返回地址)然后是arg_a在第一个 arg 之后的更高地址中?
我猜这个结果是因为编译器优化的原因,还是有其他原因?
【问题讨论】:
-
(a) C 实现在寄存器中传递一些参数是很常见的,在这种情况下它们根本不会在堆栈上。 (如果取了参数的地址,编译器会将对应的寄存器存入栈中为其创建地址。问题代码中没有取地址,所以编译器没有这样做。) (b)即使参数在堆栈上传递,C 标准也没有定义尝试使用像这样的指针黑客访问它们的行为。允许编译器优化代码而不考虑此处尝试的内容。
-
基本上,变量参数机制必须通过
<stdarg.h>中声明的设施来访问。无法通过指针黑客来实现它。 -
另外,栈可以在内存中向下增长。堆栈上的某个地方可能有一个返回地址。还有那些局部变量。 Var args 的传递方式可能比一次一个推入堆栈更复杂......我会坚持使用
va_start, va_end, va_arg。 -
@KhaledGaber:在
test中,值'a'作为参数传递。该参数对应于声明中的...。test例程中没有任何内容获取该参数的地址。 (没有什么可以,因为没有可以形成&name表达式的参数名称,因为声明中只有...,并且没有使用用于访问参数的<stdarg.h>功能。)所以参数只是留在寄存器中;它不会被复制到堆栈中。 -
当我使用 gcc 构建此代码时,我收到以下警告:
printf("%c", *arg_b);: warning: '*((void *)&a+4)' is used在此函数 [-Wuninitialized]| 中未初始化,即使它生成,Code::Blocks 反汇编程序也无法生成void test()函数的程序集。
标签: c pointers stack variadic-functions