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