首先,让我澄清一下,我知道传递指针作为这些printf
说明符的参数是不正确的。然而,我很感兴趣知道printf
完成后会发生什么。
使用普通打印说明符,%d
用于int
,%f
用于float
,为什么它是int *
打印,而float *
不会?
例如,给定这些变量(单元化):
int a, *pA;
float b, *pB;
pA = &a;
pB = &b;
当这样调用时:
void printVar(int *a, float *b)
{
printf("%d\n", a);//why does "a" print at all?
printf("%d %p\n", a, b);// when "b" prints only using %p
//printf("%d %f", a, b);// but fails on parameter mismatch using %f
printf("%d %f\n" , *a, *b);// prints normally (as expected)
}
为什么我会这样?:(“a”打印正常,但“b”仅打印%p或通过* b打印)
[edit] 整个代码,以澄清和解决一些评论问题:
#include <ansi_c.h>
void printVar(int *a, float *b)
{
printf("%d\n", a);//why does "a" print at all?
printf("%d %p\n", a, b);// when "b" prints only using %p
//printf("%d %f", a, b);// but fails on parameter mismatch using %f
printf("%d %f\n" , *a, *b);// prints normally (as expected)
}
int main()
{
int a, *pA;
float b, *pB;
char s[100], *pS;
pA = &a;
pB = &b;
pS = &s[0];
printVar(pA, pB);
getchar();
return 0;
}
*** [编辑2]解决有关实际内容的一些评论,如果取消注释3rd printf
我得到以下两个运行时通知,然后第3行的printf没有输出:
答案 0 :(得分:4)
您有以下参数:
int *a, float *b
此:
printf("%d\n", a);
最有可能将a
(类型为int*
)占用的内存空间视为,它是int
类型的对象。在许多系统上,这将给你一个几乎有意义的结果。 (当然,如果你真的想要打印一个指针值,你需要将其转换为void*
并使用%p
,但是你要问的是你的错误代码的行为,而不是如何解决它。)
如果int
和int*
的大小不同,或者int
和指针以不同的方式作为参数传递(某些CPU具有专用地址和/或例如,浮点寄存器。
printf("%d %p\n", a, b);
void*
和float*
很可能具有相同的表示形式,但语言无法保证。如果int
和int*
碰巧大小相同,并且使用相同的参数传递约定传递,则很可能会打印指针a
的内容,就像它是一个int
对象,然后将b
的值打印为指针。
//printf("%d %f", a, b);// but fails on parameter mismatch using %f
"%f"
需要double
类型的参数,类型为float
的不(float
参数会提升为double
可变函数,如printf
)。 如果 int
和int*
的大小相同,和 double
和float*
的大小相同, >和使用相同的参数传递约定传递所有这些类型,然后这很可能会打印指针对象a
的值,就像它是int
一样,并且值指针对象b
,就像它是double
对象一样。后者可能会给你浮点值,它完全是垃圾,甚至可能是NaN或Infinity。但是,如果这些假设中的任何一个失败,printf
可能会以某些顺序从堆栈(或寄存器)中获取数据,这些数据可能与参数值匹配,也可能不匹配。
上述printf
调用中的每一个都有未定义的行为,这意味着,严格来说,您应该对将要发生的事情没有任何期望 - 即使行为是以任何方式保持一致的。理解发生的事情可能有助于识别错误的原因(啊,垃圾浮点值看起来类似于我上个月弄乱了格式字符串时得到的),但不是真正的其他任何东西。
printf("%d %f\n" , *a, *b);// prints normally (as expected)
啊,这更像是它 - 但如果这是在之前的调用之后执行的,那么它们未定义的行为可能会使事情变得非常糟糕,即使这样也行不通。此外,查看整个程序,a
中的变量b
和main
(其地址传递给printVar
)未初始化,因此即使在最佳情况下{ {1}}和*a
是垃圾。感谢haccks在评论中指出这一点。
答案 1 :(得分:2)
仅仅因为b
不是浮点数,只有*b
。只是一个幸运的巧合,指针实际上是整数(它们可能不是!),因此使用%d
说明符打印它们(并导致一些不太有意义的东西)。
答案 2 :(得分:2)
这很简单 - 因为参数类型与格式字符串中的预期类型不匹配,您将获得未定义的行为。任何事都可能发生,所以没有什么是令人惊讶的。
几个调用约定不会在堆栈上传递浮点参数;相反,它们通过特殊的浮点寄存器传递它们,例如x87堆栈或SSE寄存器。因此,当printf
看到%f
格式说明符时,它会尝试从这些位置读取浮点值,但是当它看到%d
格式说明符时,它会尝试读取一个整数堆栈。当您传入指针时,编译器会将其传递给堆栈。
例如,这是一个简单的函数:
void printVars(float a, float *p)
{
printf("%f\n", a);
printf("%p\n", p);
}
这是我的x86-64系统上反汇编的注释版本,它使用SSE寄存器%xmm0
等传递浮点值:
00000000004004f4 <printVars>:
;;; Function prologue
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 48 83 ec 10 sub $0x10,%rsp
;;; Set up floating-point argument in %xmm0 register
4004fc: f3 0f 11 45 fc movss %xmm0,-0x4(%rbp)
400501: 48 89 7d f0 mov %rdi,-0x10(%rbp)
400505: f3 0f 10 45 fc movss -0x4(%rbp),%xmm0
40050a: 0f 5a c0 cvtps2pd %xmm0,%xmm0
;;; Set up format string argument (%rdi) and call printf
40050d: b8 3c 06 40 00 mov $0x40063c,%eax
400512: 48 89 c7 mov %rax,%rdi
400515: b8 01 00 00 00 mov $0x1,%eax
40051a: e8 d1 fe ff ff callq 4003f0 <printf@plt>
;;; Set up format string argument (%rdi) and pointer argument (%rsi) amd
;;; call printf
40051f: b8 40 06 40 00 mov $0x400640,%eax
400524: 48 8b 55 f0 mov -0x10(%rbp),%rdx
400528: 48 89 d6 mov %rdx,%rsi
40052b: 48 89 c7 mov %rax,%rdi
40052e: b8 00 00 00 00 mov $0x0,%eax
400533: e8 b8 fe ff ff callq 4003f0 <printf@plt>
;;; Function epilogue
400538: c9 leaveq
400539: c3 retq
答案 3 :(得分:1)
让我们逐行检查代码
void printVar(int *a, float *b, char *s)
{
printf("%d\n", a); // here, printf print address of a as an int
printf("%d %p\n", a, b); // address of b is not a float number
printf("%d %f\n" , *a, *b);// *a is a int, *b is a float number
}
答案 4 :(得分:0)
此程序修改的行为可能具有启发性。
#include <stdio.h>
void printVar(int *a, float *b)
{
printf("%d %e %p\n", a, a, a);
printf("%d %e %p\n", b, b, b);
printf("%d %e %p\n", *a, *a, *a);
printf("%d %e %p\n", *b, *b, *b);
}
int
main(void)
{
int a = 0x44444444;
float b = 5.019220152e+33;
printVar(&a, &b);
return 0;
}