float *和int *的不同printf行为?

时间:2013-10-04 21:16:59

标签: c printf format-specifiers

首先,让我澄清一下,我知道传递指针作为这些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打印)

enter image description here

[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没有输出:
enter image description here
enter image description here

5 个答案:

答案 0 :(得分:4)

您有以下参数:

int *a, float *b

此:

printf("%d\n", a);

最有可能将a(类型为int*占用的内存空间视为,它是int类型的对象。在许多系统上,这将给你一个几乎有意义的结果。 (当然,如果你真的想要打印一个指针值,你需要将其转换为void*并使用%p,但是你要问的是你的错误代码的行为,而不是如何解决它。)

如果intint*的大小不同,或者int和指针以不同的方式作为参数传递(某些CPU具有专用地址和/或例如,浮点寄存器。

printf("%d  %p\n",   a,  b);

void*float*很可能具有相同的表示形式,但语言无法保证。如果intint*碰巧大小相同,并且使用相同的参数传递约定传递,则很可能会打印指针a的内容,就像它是一个int对象,然后将b的值打印为指针。

//printf("%d  %f", a,  b);// but fails on parameter mismatch using %f

"%f"需要double类型的参数,类型为floatfloat参数会提升为double可变函数,如printf)。 如果 intint*的大小相同, doublefloat*的大小相同, >和使用相同的参数传递约定传递所有这些类型,然后这很可能会打印指针对象a的值,就像它是int一样,并且值指针对象b,就像它是double对象一样。后者可能会给你浮点值,它完全是垃圾,甚至可能是NaN或Infinity。但是,如果这些假设中的任何一个失败,printf可能会以某些顺序从堆栈(或寄存器)中获取数据,这些数据可能与参数值匹配,也可能不匹配。

上述printf调用中的每一个都有未定义的行为,这意味着,严格来说,您应该对将要发生的事情没有任何期望 - 即使行为是以任何方式保持一致的。理解发生的事情可能有助于识别错误的原因(啊,垃圾浮点值看起来类似于我上个月弄乱了格式字符串时得到的),但不是真正的其他任何东西。

printf("%d  %f\n"  ,  *a, *b);// prints normally (as expected)

啊,这更像是它 - 但如果这是在之前的调用之后执行的,那么它们未定义的行为可能会使事情变得非常糟糕,即使这样也行不通。此外,查看整个程序,a中的变量bmain(其地址传递给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;
}