Home > Mobile >  I have a question about printf() function
I have a question about printf() function

Time:11-25

I want to know why the second print function can print a? and the third print function not print a?

#include <stdio.h>

int main() {
    int i = 97; // a

    printf(&i); // a:print content in address &i  
    printf("\n%s\n", &i); // why print a?
    printf("%c\n", &i); // why not print a?
}

I want to understand printf function from pointer and memory. Thank you

CodePudding user response:

You are lucky that the first example works at all; it will print until the end of the string, and since your pointer is not to a string it will continue until it finds a zero character or produces an access error.

Same problem with example 2. I'm not sure why it doesn't work the same as example 1.

Example 3 is simple - the %c expects a single character as input, but you're giving a pointer. It would be an extremely unlikely coincidence if the pointer had the same value as your character.

CodePudding user response:

These are fun mistakes to make:

printf(&i); // a:print content in address &i

The first argument to printf() should always be a string (e.g. a const char *), but you are passing in an int * instead. Since printf() is a variadic function it has no good way of knowing that the pointer is pointing to the wrong type of data, so it will happily try to interpret the binary bytes of i (and possibly beyond it) as ASCII characters, resulting in some interesting but not-very-useful output, and potentially a crash. If an a shows up in the output, it's because you are running on a machine with a little-endian CPU, so (assuming a 32-bit CPU) the bytes of the int with value 97 are laid out like this in memory:

0x61, 0x00, 0x00, 0x00

... which you will notice just happens to be the same byte-pattern as a NUL-terminated ASCII string "a" (followed by two extra NUL bytes). If you were running on a big-endian machine instead (e.g. a PowerPC Mac), then the bytes of the int would be laid out like this:

0x00, 0x00, 0x00, 0x61

... and in that case nothing would be printed because printf() would hit the first 0x00 byte first and therefore (mis)interpret the data as an empty/0-character string.

printf("\n%s\n", &i); // why print a?

This is the same as the previous example, except that you've (correctly) provided a formatting-string before (incorrectly) passing a pointer-to-int in the second argument (where the %s means that printf() was expecting a pointer-to-chars argument instead)

printf("%c\n", &i); // why not print a?

In this case you're passing a pointer-value, but the %c in the format-string means that printf() was expecting an integer as the second argument. Therefore printf() is interpreting the memory-address as an integer, likely casting it to a char, which means that you're likely to get whatever ASCII character corresponds to the lowest 7 or 8 bits of the pointer.

None of these patterns is very useful in practice, except as a way to demonstrate the lack of type-safety in printf()'s argument-passing conventions... but for that, they are very useful :)

CodePudding user response:

All three examples are technically incorrect; given a character code in an int variable i, the correct way to print it with printf is

printf("%c\n", i); // pass the _value_, not the address of i

(You might be confused because when you use scanf you do need to pass the address of i. That's because scanf writes to the variable. "Out parameters" in C always need to be pointers.)

The first example is also incorrect because it's dangerous to use anything but a string literal as the first argument to printf. (There are times when you have to pass a string that varies at runtime, but don't worry about that until you trip over a situation that requires it.)

Anyway, let's peel back the "undefined behavior" veil and look at what's actually happening. This modification of your second example

unsigned int i = 'a';  // == 97, assuming ASCII
printf("%s\n", (char *)&i);

does not have undefined behavior (merely "implementation defined" behavior) and should, on almost all modern computers, compile to exactly the same code as your second example. On a majority of modern computers it will print "a" and on the remainder it will print a blank line. Here's why: int is almost certainly either two or four bytes. When you store 'a' in an int, the numeric value of the character constant (let's go ahead and assume ASCII, so 97 == 0x61) will be zero-extended to the width of int, either 0x0061 or 0x00000061. When you take the address of the variable you force it into memory, where it will be stored as a sequence of bytes. But in what order? A majority of computers nowadays are "little endian", meaning that the lowest-value bits of the number are stored at the lowest address, so we have either the byte sequence 0x61 0x00 or 0x61 0x00 0x00 0x00. And that is identical to the byte sequence you'd have in memory for the C string "a", possibly followed by some junk, which is what you told printf it was to print.

The alternative to little-endian is big-endian, in which the lowest-value bits are stored at the highest address; in that case you'd have [0x00 0x00] 0x00 0x61 and printf would see that leading 0x00 and interpret it as having been passed an empty string and so it would only print a blank line.

More or less the same thing may have happened with your first example, since either "a" or "" is a format string with no substitutions if used as the first argument to printf, and extra arguments to printf are ignored.

The third example, on the other hand, hands printf a pointer value (we have no way of knowing what the actual number is) and tells it to interpret that as a character constant. Depending on how the "calling convention" works on your computer, printf might not even look for the number where the caller put it! All we can say about this is, it's very likely not to print a, because the %c format code is never going to dereference the pointer.

CodePudding user response:

It depends on what is 'expected'.

If it's an address (%s), it will go to and read the contents of that address.

If it's a value (%c), it won't go anywhere and take it literally.

  • Related