Reputation: 2642
I am doing some exercises from book "Understanding pointers in C". The book gives you a piece of code and asks you for what you get in output.
One of them is the following:
#include <stdio.h>
int main(int argc, char* argv[])
{
char c, *cc;
int i;
long l;
float f;
c = 'Z';
i = 15;
l = 77777;
f = 3.14;
cc = &c;
printf("c=%c cc=%u\n", *cc, cc);
cc = &i;
printf("i=%d cc=%u\n",*cc,cc);
cc=&l;
printf("l=%ld cc=%u\n",*cc,cc);
cc=&f;
printf("f=%f cc=%u\n",*cc,cc);
return 0;
}
and the output is:
c=Z cc=1946293623
i=15 cc=1946293616
l=4294967249 cc=1946293608
f=0.000000 cc=4294967235
I don't understand why l
and f
are not printed as 77777
and 3.14
?
I checked the book The C Programming Language to see if the if the printf
's control chars are correct, and they are.
Upvotes: 2
Views: 5833
Reputation: 263537
As Bryan's answer says, the types of the arguments passed to printf
need to match the types specified by the format string.
The format to print a pointer value is "%p"
-- and it expects a void*
argument; a pointer of another type should be explicitly cast to void*
.
Your program has undefined behavior because of the type mismatches. It also has constraint violations because it attempts to assign incompatible pointer types without a cast; cc = &i;
is illegal (but a compiler can merely warn about it if it chooses). You should have gotten several warnings from your compiler.
I hope the intent of that program is to demonstrate what not to do. Let's consider just the last two lines before the return
statement:
cc=&f;
printf("f=%f cc=%u\n",*cc,cc);
cc = &f;
violates a constraint and requires a diagnostic (IMHO a good compiler should give you a fatal error and reject the program). &f
is of type float*
, and cc
is of type char*
; those types are not assignment-compatible.
Most compilers that accept that statement assume it implies an implicit conversion, making it equivalent to the legal but tricky:
cc = (char*)&f;
This causes cc
to point to the first byte of the float
object f
.
printf("f=%f cc=%u\n",*cc,cc);
This claims to print the value of f
, but it doesn't; it prints the value of the char
object that cc
points to, which happens to be the first byte of f
. And it uses a %f
format for that char
object, which has undefined behavior. The result is garbage.
I was going to write a "corrected" version of that program, but it has so many problems, I couldn't figure out just what it's intended to do.
I checked the book The C programming language to see if the if the printf's control chars are correct, and they are.
No, they aren't. "%f"
is correct for printing a float
or double
argument (float
is promoted to double
in this context), but you're passing a char
argument.
This would be correct:
printf("*cc = %d, cc = %p\n", *cc, (void*)cc);
but it doesn't do the same thing; it prints the char pointed to by cc
as an int
(you could use %c
to print it as a character, but it's likely to be unprintable), and then prints the value of cc
as a pointer.
It would be interesting to see what the book says about this program.
Upvotes: 4
Reputation: 3663
So many things that are wrong with your program!!!
First things first, don't use the %u
format specifier for pointers.unsigned integers
and pointers
needn't essentially have the same size. Always use %p
for pointers
. For example,
printf("c=%c cc=%p\n", *cc, cc);
The second most important thing—never use that book "Understanding pointers in C" by YK (I can't name the author in full due to obvious reasons). His other book is notorious as the "void main" book here on SO.
Third, coming back to your program, I am surprised you didn't get any warnings while you assign addresses of integer and float variables to a character pointer, without a cast.. This is bound to produce errors as your program won't be able to know how many bytes to interpret from the pointed location. int
, float
, and char
vary in sizes. And you may wonder why you didn't get error for int
then? Right? It's because of the little-endian architecture of your computer, whereby the least significant bytes are stored first.
So here's your correction:
cc = (char*) &i;
printf("i=%d cc=%p\n", *(int*)cc, cc);
cc = (char*)&l;
printf("l=%ld cc=%p\n", *(long*)cc, cc);
cc = (char*)&f;
printf("f=%f cc=%p\n", *(float*)cc, cc);
Upvotes: 2
Reputation: 5655
Consider:
printf("l=%ld cc=%u\n", *(long*)cc, cc);
printf("f=%f cc=%u\n", *(float*)cc, cc);
This works, but
printf("l=%ld cc=%u\n", *cc, cc);
printf("f=%f cc=%u\n", *cc, cc);
does not work as expected because of the following reasons.
Every pointer is of 4 bytes (it depends on the OS, not on where it is pointing.)
So even an address of float can be stored into char *
.
But when we do pointer arithmetics or when we read the value of pointer *cc
, it reads the number of characters based on the type of the pointer...
Suppose a character is of two bytes, then *cc
will read only two bytes with address in cc
whereas the float or long number has been stored in four bytes (or more).
So it will show invalid data.
When we typecast it *(long*)cc
, then it’s treated as a long pointer with explicit typecasting.
Upvotes: 2
Reputation: 5307
The format specifiers of printf
(and worse so scanf
) have to match the types passed as parameters. If you are using GCC then use the -Wall option and it will also give warnings about this. Your code should look like this:
printf("c=%c cc=%u\n", *cc, (unsigned int)cc);
cc = &i;
Taking the address of i
into a char*
interpretation doesn't make sense anymore and neither does it for the others below. By assigning pointers, you enforce a wrong interpretation of the same bit representation.
Upvotes: 3
Reputation: 40145
Try this:
printf("l=%ld cc=%u\n", *(long*)cc, cc);
printf("f=%f cc=%u\n", *(float*)cc, cc);
Upvotes: 1