Question

First, let me clarify that I know passing pointers as arguments to these printf specifiers is not correct. I am interested however in knowing what is going on in printf when it is done.

Using the normal print specifiers, %d for int, and %f for float, why is it the int * prints, while the float * will not?

For example, given these variables (unitialized):

int a, *pA;
float b, *pB;

pA = &a;  
pB = &b;

When called like this:

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)
}

Why do I get this?: ("a" prints OK, but "b" prints only with %p or by passing *b)

enter image description here

[edit] entire code to clarify and address some of the comment questions:

#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;
}

***[edit 2] addressing some comments about actual content if uncomment 3rd printf

I get the following two run-time notices, then get no output for the printf in line 3:
enter image description here
enter image description here

Was it helpful?

Solution

You have these parameters:

int *a, float *b

This:

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

most likely treats the memory space occupied by a (which is of type int*) as if it were an object of type int. On many systems, that will give you an almost meaningful result. (Of course if you really want to print a pointer value, you need to cast it to void* and use %p, but you're asking about the behavior of your incorrect code, not how to fix it.)

Things can get strange if int and int* are not the same size, or if ints and pointers are passed differently as arguments (some CPUs have had dedicated address and/or floating-point registers, for example).

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

It's very likely that void* and float* have the same representation, though that's not guaranteed by the language. If int and int* happen to be the same size, and are passed using the same argument-passing convention, this will most likely print the contents of the pointer a as if it were an int object, and then print the value of b as a pointer.

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

"%f" requires an argument of type double, not of type float (float arguments are promoted to double for variadic functions like printf). If int and int* are the same size, and double and float* are the same size, and all these types are passed using the same argument-passing convention, then this will most likely print the value of the pointer object a as if it were an int, and the value of the pointer object b as if it were a double object. The latter is likely to give you floating-point value that's completely garbage, perhaps even a NaN or Infinity. But if any of these assumptions fail, printf will likely grab data off the stack (or from registers) in some order that may or may not match up with the argument values.

Every one of the above printf calls has undefined behavior, which means that, strictly speaking, you should have no expectations about what will happen -- even that the behavior is in any way consistent. Understanding what's going on under the covers can be useful in recognizing the cause of an error (ah, that garbage floating-point value looks similar to what I got when I messed up a format string last month), but not really for anything else.

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

Ah, that's more like it -- but if this is executed after the previous calls, their undefined behavior may mess things up so badly that even this doesn't work. Also, looking at your entire program, the variables a and b in main (whose addresses are passed to printVar) are not initialized, so even in the best case *a and *b are garbage. Thanks to haccks for pointing this out in a comment.

OTHER TIPS

Simply because b is not a float, only *b is. It is just a lucky coincidence that pointers are actually integers (they might not be!) and thus printing them using %d specifier works (and results in something not quite meaningful).

It's quite simple -- because the argument types don't match the expected types from the format string, you get Undefined Behavior. Anything at all could happen, so nothing should be surprising.

Several calling conventions don't pass floating-point parameters on the stack; rather, they pass them through special floating-point registers, e.g. the x87 stack or SSE registers. So when printf sees a %f format specifier, it tries to read a floating-point value from those locations, but when it sees a %d format specifier, it tries to read an integer off the stack. When you pass in a pointer instead, the compiler passes that on the stack.

For example, here's a simple function:

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

And here's an annotated version of its disassembly on my x86-64 system, which passes floating-point values using the SSE registers %xmm0 etc.:

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 

Let us examine the code line by line

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
}

The behavior of this modification of your program may be enlightening.

#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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top