cstringinteger

Why do a string and integer with identical memory values print differently?


I'm making my way through Learn C the Hard Way, in which the author often asks students to intentionally "break" the examples to learn debugging. In Exercise 11, it's suggested to attempt to print a string as an integer. Simple enough, but in the following code, why doesn't name output the same as number when both are printed as integers?

My code, simplified:

#include <stdio.h>

int main() {
   // This should be equal to 5318008, according to my terrible Python code.
   char name[4] = {'x', '%', 'Q', 0};
   int number = 5318008;
   printf("%d\n", name);
   printf("%d\n", number);
   return 0;
}

Output when run:

% ./temp
1962793460
5318008

The output of gdb, showing these variables are the same in memory (as best I can tell with my limited experience):

% gdb --quiet ./temp
Reading symbols from ./temp...
(gdb) break 7
Breakpoint 1 at 0x116e: file ./temp.c, line 7.
(gdb) run
Starting program: /home/asdfadf/Documents/c/temp

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.archlinux.org>
Enable debuginfod for this session? (y or [n])
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, main () at ./temp.c:7
7          printf("%d\n", name);
(gdb) x/4xb &name
0x7fffffffd9e4: 0x78    0x25    0x51    0x00
(gdb) x/4xb &number
0x7fffffffd9e0: 0x78    0x25    0x51    0x00
(gdb)

Solution

  • printf("%d\n", name); does not pass the bytes in name to printf. It passes the address of name (technically the address of its first element) to printf.

    For %d, printf expects an int. When you pass an address instead, the behavior is not defined by the C standard. Often (but not always), the result is to print the address, or some portion of it, as a decimal numeral. This numeral is unrelated to whatever values were stored in memory at the address.

    When you pass a string to printf with a %s specification, the address is also passed, but printf knows that, for a string, it must fetch the bytes from memory. So it takes the address that it was passed and uses it to load bytes from memory and print them. This does not occur with %d; the value for %d must be passed directly, not via an address.

    If you want to reinterpret the bytes in name as a number, you can do that as shown here:

    #include <stdio.h>
    #include <string.h> // Declares memcpy.
    
    int main(void)
    {
       char name[4] = {'x', '%', 'Q', 0};
       int t;                       // Define a temporary variable.
       memcpy(&t, name, sizeof t);  // Copy bytes into t.
       printf("%d\n", t);           // Show value of t.
    }
    

    Note that different C implementations may represent int in different ways, notably by using different orders for the bytes in it, so different C implementations may print different values. Different C implementations may also use fewer or more than four bytes for int. The values of the characters “x”, “%”, and “Q” may also vary.