假设在我的平台上sizeof(int)==sizeof(void*)
,我有以下代码:
printf( "%p", rand() );
由于传递的值不是
%p
而不是有效的指针,这将是未定义的行为吗?
最佳答案
为了扩展@larsman的答案(这表示由于您违反了约束,因此行为未定义),这是一个实际的C实现,其中sizeof(int) == sizeof(void*)
,但是该代码并不等效于printf( "%p", (void*)rand() );
摩托罗拉68000处理器具有16个用于常规计算的寄存器,但它们并不等效。其中八个(从a0
到a7
命名)用于访问内存(地址寄存器),另外八个(从d0
到d7
命名)用于算术(数据寄存器)。此架构的有效调用约定为
d0
和d1
中传递前两个整数参数;将其余的传递给堆栈。 a0
和a1
中传递前两个指针参数;将其余的传递给堆栈。 这是一个完全合法的调用约定,类似于许多现代处理器使用的调用约定。
例如,要调用函数
void foo(int i, void *p)
,您可以在i
中传递d0
,在p
中传递a0
。请注意,要调用函数
void bar(void *p, int i)
,您还将在i
中传递d0
并在p
中传递a0
。在这些规则下,
printf("%p", rand())
将在a0
中传递格式字符串,并在d0
中传递随机数参数。另一方面,printf("%p", (void*)rand())
将在a0
中传递格式字符串,并在a1
中传递随机指针参数。va_list
结构如下所示:struct va_list {
int d0;
int d1;
int a0;
int a1;
char *stackParameters;
int intsUsed;
int pointersUsed;
};
前四个成员用相应的寄存器入口值初始化。
stackParameters
指向通过...
传递的第一个基于堆栈的参数,并且intsUsed
和pointersUsed
初始化为命名参数的数量,它们分别是整数和指针。va_arg
宏是编译器固有的,可根据预期的参数类型生成不同的代码。va_arg(ap, T)
扩展为(T*)get_pointer_arg(&ap)
。 va_arg(ap, T)
扩展为(T)get_integer_arg(&ap)
。 va_arg(ap, T)
扩展为*(T*)get_other_arg(&ap, sizeof(T))
。 get_pointer_arg
函数如下所示:void *get_pointer_arg(va_list *ap)
{
void *p;
switch (ap->pointersUsed++) {
case 0: p = ap->a0; break;
case 1: p = ap->a1; break;
case 2: p = *(void**)get_other_arg(ap, sizeof(p)); break;
}
return p;
}
get_integer_arg
函数如下所示:int get_integer_arg(va_list *ap)
{
int i;
switch (ap->intsUsed++) {
case 0: i = ap->d0; break;
case 1: i = ap->d1; break;
case 2: i = *(int*)get_other_arg(ap, sizeof(i)); break;
}
return i;
}
get_other_arg
函数如下所示:void *get_other_arg(va_list *ap, size_t size)
{
void *p = ap->stackParameters;
ap->stackParameters += ((size + 3) & ~3);
return p;
}
如前所述,调用
printf("%p", rand())
将在a0
中传递格式字符串,并在d0
中传递随机整数。但是,当printf
函数执行时,它将看到%p
格式并执行va_arg(ap, void*)
,后者将使用get_pointer_arg
并从a1
而不是d0
读取参数。由于a1
未初始化,因此包含垃圾。您生成的随机数将被忽略。进一步举例说明,如果您有
printf("%p %i %s", rand(), 0, "hello");
,则将其称为:a0
=格式字符串的地址(第一个指针参数)a1
=字符串"hello"
的地址(第二个指针参数)d0
=随机数(第一个整数参数)d1
= 0(第二个整数参数)当
printf
函数执行时,它将按预期从a0
中读取格式字符串。当看到%p
时,它将从a1
中检索指针并打印出来,因此您获得了字符串"hello"
的地址。然后它将看到%i
并从d0
检索参数,因此它将打印一个随机数。最后,它看到%s
并从堆栈中检索参数。但是您没有在堆栈上传递任何参数!这将读取未定义的堆栈垃圾,这很可能在您的程序尝试打印它时就好像是一个字符串指针一样使您的程序崩溃。
关于c++ - %p说明符仅用于有效的指针吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11688162/