-1

首先,让我澄清一下,我知道将指针作为参数传递给这些printf说明符是不正确的。然而,我有兴趣知道printf它完成后会发生什么。

使用普通的打印说明符%dforint%ffor 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 打印)

在此处输入图像描述

[编辑] 整个代码以澄清和解决一些评论问题:

#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 的输出:
在此处输入图像描述
在此处输入图像描述

4

5 回答 5

4

你有这些参数:

int *a, float *b

这:

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

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

如果大小不同,或者sint和指针作为参数传递的方式不同(例如,某些 CPU 具有专用地址和/或浮点寄存器),事情就会变得奇怪。int*int

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

很有可能void*并且float*具有相同的表示形式,尽管语言不能保证这一点。如果intint*恰好大小相同,并且使用相同的参数传递约定进行传递,则很可能将指针的内容打印aint对象,然后将值打印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)

啊,这更像是它——但如果这是在之前的调用之后执行的,它们未定义的行为可能会把事情搞得一团糟,以至于即使这样也不起作用。此外,查看您的整个程序,变量abin main(其地址传递给printVar)未初始化,因此即使在最好的情况下*a也是*b垃圾。感谢黑客在评论中指出这一点。

于 2013-10-04T21:54:45.097 回答
2

只是因为b不是浮动,只有*b是。指针实际上是整数(它们可能不是!),因此使用%d说明符工作(并导致一些不太有意义的结果)打印它们只是一个幸运的巧合。

于 2013-10-04T21:22:11.887 回答
2

这很简单——因为参数类型与格式字符串中的预期类型不匹配,你会得到未定义的行为。任何事情都有可能发生,所以没有什么是令人惊讶的。

一些调用约定不在堆栈上传递浮点参数;相反,它们通过特殊的浮点寄存器传递它们,例如 x87 堆栈或 SSE 寄存器。因此,当printf看到%f格式说明符时,它会尝试从这些位置读取浮点值,但当它看到%d格式说明符时,它会尝试从堆栈中读取整数。相反,当您传入指针时,编译器会将其传递到堆栈上。

例如,这是一个简单的函数:

void printVars(float a, float *p)
{
  printf("%f\n", a);
  printf("%p\n", p);
}

%xmm0这是我的 x86-64 系统上它的反汇编的注释版本,它使用 SSE 寄存器等传递浮点值:

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 
于 2013-10-04T22:12:04.203 回答
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
}
于 2013-10-04T21:21:01.727 回答
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;
}
于 2013-10-04T21:25:57.753 回答