Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unsigned integer print

Tags:

c

printf

Why does this print -1

#include <stdio.h>

int main(){
    unsigned int i = -1;
    printf("%d", i);
    return 0;
}

While this prints

#include <stdio.h>

int main(){
    unsigned int i = -1;
    printf("%u", i);
    return 0;
}

the largest possible integer value

Also, why does this

#include <stdio.h>

int main(){
    int c = printf("Hello");
    printf("%d", c);
    return 0;
}

Print hello5 and not 5.

Counter-statement //for Eric

You mentioned about side-effect, right?

#include <stdio.h>
int main(){
int i = 0;
for (; i++; printf("%d", i));
printf("%d", i);
return 0; 
}

Why does the printf ("%d",i) inside the for loop not print the value in this code as a side-effect?

like image 494
programmer Avatar asked Sep 06 '25 03:09

programmer


1 Answers

Question 1

Why does this print -1…

unsigned int i=-1;
printf ("%d",i);

Formally, the behavior is not defined by the C standard, because i is an unsigned int but %d is for an int, and C 2018 7.21.6.1 9 says “If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.”

However, C has a history of conflating signed and unsigned types1, and many compilers tread lightly in this regard.2 Typically, what happens is:

  • The function call passes the unsigned int value of i by putting its bits in the place where an unsigned int argument should be passed.
  • The printf gets the bits for an int from the place where an int argument should be passed.
  • These are the same place.
  • In consequence, printf reinterprets the bits that represent an unsigned int as if they were an int.

In unsigned int i = -1, −1 is converted to unsigned int according to rules in the C standard (6.3.1.3, see below) that result in the maximum unsigned int value, which has all bits set in its representation: 1111…11112.

In reinterpreting those bits as int, most C implementations use two’s complement. In two’s complement, the bits 1111…11112 represent −1.

So printf takes the passed bits as the int value −1 and prints “-1”.

Question 2

While this prints…

unsigned int i=-1;
printf ("%u",i);

… the largest possible integer value

The rule for converting −1 to unsigned int is in 6.3.1.3 2:

… the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

“One more than the maximum value that can be represented in the new type” is 1+UINT_MAX. Adding −1 to 1 + UINT_MAX produces UINT_MAX, which is representable in unsigned int, so that is the value stored in i. Printing this with %u prints its value normally.

Question 3

Also, why does this…

int c=printf ("Hello");
printf ("%d",c);

… Print hello5 and not 5.

printf does two things:

  • It writes characters to standard output. (The C standard calls this a side effect because it is something the function does besides returning a value.)
  • It returns the number of characters written (or a negative value if an error occurred).

When you call printf("Hello"), it writes “Hello” to output and returns 5.

Then printf("%d", c) writes “5” to output.

The question suggests you think that using printf in an assignment or initialization causes only its return value to be used, suppressing the side effect. This is not so. Whenever an expression is evaluated in C, its side effect and main effect are evaluated.

(In fact, assignments are themselves expressions; c = printf("Hello") is an expression, and you can use it in further operations, such as x = 3 * (c = 5), which will assign 15 to x. We can take any expression and put a semicolon after it to make an expression statement, which evaluates the expression and discards the main result. printf("%d", c); evaluates printf for its side effect and discards the return value.)

Footnotes

1 For example, 6.5.2.2 6 allows substituting a signed integer type for the corresponding unsigned integer type and vice-versa in calls to functions without prototypes, albeit limited to values representable in both. For prototypes with ..., 7.16.1.1 allows the same substitution when using va_arg. The rules in 6.2.6.2 require value bits common to signed and unsigned types to have the same values.

2 I do not recall any formal statement in regard to conflating signed and unsigned types, but I know compiler designers take some care to avoid breaking code with certain legacy use or certain not uncommon use cases even if the C standard does not require supporting them. In C, it is easy to slip and pass an unsigned char to be printed with %u, which is technically wrong because the unsigned char will be promoted to int, not unsigned int.

like image 195
Eric Postpischil Avatar answered Sep 07 '25 23:09

Eric Postpischil



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!