Compiler gives warning when printing the address of a variable - c

I made a very simple program to print the address of two variables.
#include<stdio.h>
int main()
{
int a,b;
printf("%u\n%u",&a,&b);
return 0;
}
But, the Clang-3.7 compiler gives warning as:
warning: format specifies type 'unsigned int' but the argument has type 'int *' [-Wformat]`
But, when I compiled with GCC-5.x, it gave no warnings. Which of them is correct?
One thing I know is that doing unsigned int num=&a; would be wrong as address can only be stored in a pointer. But, is it correct for the compiler to give warning when printing the address?
I compiled my program from gcc.godbolt.org

%p is the correct format specifier to print addresses:
printf("%p\n%p",(void*)&a, (void*)&b);
The C standard requires that the argument corresponding to %p should be of type void*. So the casts are there.
C11, Reference:
p The argument shall be a pointer to void. The value of the pointer
is converted to a sequence of printing characters, in an
implementation-defined manner.
Using incorrect format specifier is undefined behavior. A compiler is not required to produce any diagnostics for undefined behaviors. So both are gcc and clang are correct.
GCC 5.1 does produce warnings on my system without any additional
options. And GCC godbolt produces warnings with stricter compiler options: -Wall -Wextra. In general, you should compile with strictest compiler options.

The correct format specifier for printing an address (pointer) is %p and you need to cast the argument to void *.
Hence, the warning is valid and should be there.
But, when I compiled with GCC-5.x, it gave no warnings
In case of gcc, please include -WallNote compiler option and try to compile. I believe it will throw the (same) warning we're expecting.
Note: Actually, -Wformat, which checks for the type of supplied argument to printf() and scanf() family calls. -Wall enables -Wformat.

You got this error because format-specifier %u accepts unsigned-integer, whereas what you are supplying in your code is a pointer to a memory location.
You can print address of memory location held by pointer by specifying type of the argument as (void*), this will generate no errors and print the address of memory location in decimal format.
printf("%u\n%u",(void*)&a,(void*)&b);
Also, this is not the correct way to print a pointer, the correct way is to use specifier %p, which will print the address of the memory location in hexadecimal format.
printf("%p\n%p",(void*)&a,(void*)&b);
Hope that helps.

Related

How does assigning a string to int and passing that int to printf prints the string properly?

Why does this work? (i.e how is passing int to printf() results in printing the string)
#include<stdio.h>
int main() {
int n="String";
printf("%s",n);
return 0;
}
warning: initialization makes integer from pointer without a cast [enabled by default]
int n="String";
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s",n);
Output:String
compiler:gcc 4.8.5
In your code,
int n="String"; //conversion of pointer to integer
is highly-implementation dependent note 1
and
printf("%s",n); //passing incompatible type of argument
invokes undefined behavior. note 2
Don't do that.
Moral of the story: The warnings are there for a reason, pay heed to them.
Note 1:
Quoting C11, chapter §6.3.2.3
Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. [....]
Note 2:
chapter §7.21.6.1
[....] If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
and for the type of argument for %s format specifier with printf()
s If no l length modifier is present, the argument shall be a pointer to the initial
element of an array of character type. [...]
The behaviour of your program is undefined.
Essentially, you're assigning a const char* to an int, and the printf converts it back. But do regard that as entirely coincidental: you're not allowed to cast unrelated types like that.
C gives you the ability to shoot yourself in the foot.
int type can store 4 bytes numbers on most today's computers (from -2147483647 to 2147483647)
Which means it ""can"" store SOME address as well, only problem is when your address is bigger than 2147483647 it will cause overflow and you will not be able to get the address, (which is very bad for your program obviously)
An address is a number referring to memory space,
Pointers are made to store addresses, they are larger (8 bytes on 64bits systems, 4 bytes on 32bits systems) and they are also unsigned (only positive)
which means, when you affect int n="String"; if the address to "String" is under 2147483647 it wont cause issues and you code will run (DONT DO THAT)
http://www.tutorialspoint.com/c_standard_library/limits_h.htm
now if you think about it, you can guess why there is a 4GB ram limit on 32bit systems
(sorry about possible english mistakes, I m french)
Compiling with options like -Wall -Wextra -Werror -Wint-to-pointer-cast -pedantic (GCC) will show you very quickly that this behaviour should not be relied upon.

Different behavior of printf() in Turbo C and gcc when trying to print a pointer

The code below gives the output without error in Turbo C compiler, and gives the address of variable and its value both:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
But when I compile the same code in Linux with GCC compiler, it gives an error:
`warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d %d\n",fp,*fp);`
But the same code with the %p format specifier works in GCC Compiler ,to which I agree. The question is: how come it's working on Turbo C platform?
P.S. The issue is not that (in Turbo C) the error is not reported but it's that on Turbo C
it gives a signed integer value that is unchanged on repeated execution of the program; can it be garbage?
P.P.S Turbo C is running on MSDOS platform and GCC on 64-bit Linux, if that helps.
printf() assumes you will pass a signed int for the %d specifier.
On the platform and compiler you are using, ints and pointers are probably the same size and printf() is able to display it correctly. Your pointer is reinterpreted as an int.
Compiling and running this on a different platform where, for example, ints are 32-bit and pointers are 64-bit (such as gcc on x64) would be undefined behaviour. If you are lucky enough, it would crash. If not, you'd get garbage.
The %d conversion specifier requires the corresponding argument to have type [signed] int. If the corresponding argument in fact has a different type then the behavior is explicitly undefined. This is the case regardless of the relative sizes of the expected and actual types, and regardless of whether implicit or explicit conversion between those types is possible.
Neither the behavior of the compiler nor that of any part of any resulting compiled program is defined when the program exhibits undefined behavior. Turbo C is not required to diagnose the problem. On the other hand, gcc is quite permitted to diagnose it, and even to reject the source code with an error instead of merely alerting you with a warning. As far as C is concerned, the whole program's behavior can be absolutely anything if any undefined behavior is triggered -- from what the author intended (whatever that may be) to emitting rude comments from the machine's speaker, and far beyond.
In practice, the undefined behavior is likely (but by no means certain) to manifest in a relatively benign way if the expected and actual types are the same size, and the conversion specifier is not %s. Otherwise, all bets are off. Note in particular that many C implementations for 64-bit platforms feature 32-bit ints and 64-bit pointers.
Each conversion specifier, such as %d, specifies both the type of the required argument and the format used to print it.
%d requires an argument of type int (equivalently signed int), and prints it in decimal.
%u requires an argument of type unsigned int and prints it in decimal.
%x requires an argument of type unsigned int and prints it in hexadecimal.
And so forth.
In your code:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
the second %d is correct, since the corresponding argument, *fp is of type int. But the first is incorrect, since the corresponding argument fp is of type int*.
If you pass an argument of the wrong type for a conversion specifier, the behavior is undefined. A compiler is not required to warn you if you do this, since it's not possible to detect the error in the most general case (the format string doesn't have to be a string literal). Some compilers, including gcc, will analyze format strings if they're string literals and warn about mismatches. Turbo C apparently does not (that's not surprising, it's a very old compiler).
The correct format for printing a pointer value is %p. This requires an argument of type void*, and prints it in an implementation-defined manner. A pointer of a type other than void* should be converted.
The correct version of your code is:
int a = 5, *fp;
fp = &a;
printf("%p %d\n", (void*)fp, *fp);
It's a warning, not an error. The first compiler could also say the same, but doesn't.
The warning doesn't stop the compilation, so it will work too. They're just different compilers. Also, compiler accepting your program doesn't mean that the program is correct.

Can int store the base address of string in C?

Why do the code run without error ?
#include <stdio.h>
int main() {
int i="string"; //the base of string can be stored in a character pointer
printf("%s\n",i);
printf("%d",i);
return 0;
}
//compiling on ideone.com language c
OUTPUT:
string
134513984 //some garbage(address of "string")
Please explain if there is some flexibility in the pointer in c. I tried it for c++ which gives error: cannot convert ‘const char*’ to ‘int*’ in initialization
No, you cannot assume this in general. In part, this is because int may not be the same size as char * (in fact, on many 64-bit compilers it will not be the same size).
If you want to store a pointer as an integer, the appropriate type to use is actually intptr_t, from <stdint.h>. This is an integer which is guaranteed to be able to hold a pointer's value.
However, the circumstances when you'd actually want to do this are somewhat rare, and when you do do this you should also include an explicit cast:
intptr_t i=(intptr_t)"string"; //the base of string can be stored in a character pointer
This also complicates printing its value, you'll need to use a macro to be portable:
printf("%"PRIiPTR,i);
To print the original string, you should also cast:
printf("%s", (char *)i);
In general, no: the C standard states that conversions from pointers to integers are implementation defined. Further, this can be problematic on systems where sizeof(char *) and sizeof(int) are different (i.e. x86-64), for two reasons:
int i = "string"; can lose information, if the e.g. 64-bit pointer cannot fit in a 32-bit integer.
printf expects a pointer to be passed in, but gets a smaller integer. It winds up reading some garbage into the full pointer, and can crash your code (or worse).
Often times, however, compilers are "smart" enough to "fix" arguments to printf. Further, you seem to be running on a platform where pointers and integers are the same size, so you got lucky.
If you compiled this program with warnings (which you should) you'd get the following complaints:
main.c:3:9: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'char [7]' [-Wint-conversion]
int i="string"; //the base of string can be stored in a character pointer
^ ~~~~~~~~
main.c:4:19: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
printf("%s\n",i);
~~ ^
%d
2 warnings generated.
Warnings generally mean you're doing something that could cause unexpected results.
Most C compilers will let you do this, but that doesn't make it a good idea. Here, the address of the character array "string" gets stored in i. The printf options are determining how the integer is interpreted (as an address or an integer). This can be problematic when char* is not the same size as an int (e.g. on most 64 bit machines).
The C++ compiler is more picky and won't let you compile code like this. C compilers are much more willing, although they will usually generate warnings letting the programmer know it is a bad idea.
Your code is ill-formed in both C and C++. It is illegal to do
int i = "string";
in both languages. In both languages conversion from a pointer to an integer requires an explicit cast.
The only reason your C compiler accepted it is that it was configured by default for rather loose error checking. (A rather typical situation with C compilers.) Tighten up your C compiler settings and it should issue an error for the above initialization. I.e. you can use an explicit conversion
int i = (int) "string";
with implementation-dependent results, but you can't legally do it implicitly.
In any case, the warning your compiler emitted for the above initialization is already a sufficient form of a diagnostic message for this violation.

printf for float behaves differently if position changed

I've below piece of code :
float i=85.00;
printf("%f %p",i,i);
which prints o/p:
85.00000 (nil)
But when I change the order like below:
float i=85.00;
printf("%p %f",i,i);
the o/p is :
(nil) 0.00000
While I expect that similar o/p should be printed as earlier, in mentioned order.
What is the behaviour going on can anyone please explain?
What you're doing is undefined behavior. The %p format specifier promises that you're passing in a pointer (specifically a void* pointer), and you're passing in a double instead (float values get promoted to double when passed to variadic functions such as printf). So as soon as you fail to satisfy the requirements, the compiler and implementation are free to do whatever they want, and printing out 0 instead of 85 in the second case is perfectly legal.
What's likely happening is that the compiler is using a calling convention which places floating-point values in separate registers (e.g. the x87 floating-point stack, or the SSE2 SIMD registers) instead of on the stack, whereas integer values like pointers get passed on the stack. So when the implementation sees a %p format specifier, it tries to read the argument off of the stack, when in fact the argument is actually elsewhere.
As others have mentioned, you should really be compiling with the -Wall option (assuming GCC or a GCC-compatible compiler). It will help you catch these kinds of errors.
printf() expects a pointer for the format specifier %p whereas you are passing the value of a float. This is undefined behaviour.
If you want to print the address, then pass the address:
printf("%f %p",i, &i);
If you turn on the warnings, you'll see the problem:
warning: format ‘%p’ expects argument of type ‘void *’, but argument 3
has type ‘double’ [-Wformat]
You are probably hitting some undefined behavior; printing a float as a pointer has absolutely no sense.
And GCC 4.8 invoked as gcc -Wall rightly warns you:
dwakar.c:5:3: warning: format '%p' expects argument of type 'void *',
but argument 3 has type 'double' [-Wformat=]
printf("%f %p",i,i);
^
So please use gcc -Wall to compile your code and correct it till no warnings are given.
Notice that at the ABI level floating point and pointers arguments are passed thru different registers. So the implementation probably prints as pointer the (uninitialized, or old) content of whatever register is involved.

Assigning a string to a variable of type int

Why is it that I can assign a string to a variable of type int? Eg. the following code compiles correctly:
int main(int argv, char** argc){
int a="Hello World";
printf(a);
}
Also, the program doesn't compile when I assign a string to a variable of a different type, namely double and char.
I suppose that what is actually going on is that the compiler executes int* a = "Hello World"; and when I write double a="Hello World";, it executes that line of code as it is.
Is this correct?
In fact, that assignment is a constraint violation, requiring a diagnostic (possibly just a warning) from any conforming C implementation. The C language standard does not define the behavior of the program.
EDIT : The constraint is in section 6.5.16.1 of the C99 standard, which describes the allowed operands for a simple assignment. The older C90 standard has essentially the same rules. Pre-ANSI C (as described in K&R1, published in 1978) did allow this particular kind of implicit conversion, but it's been invalid since the 1989 version of the language.
What probably happens if it does compile is that
int a="Hello World";
is treated as it if were
int a = (int)"Hello World";
The cast takes a pointer value and converts to int. The meaning of such a conversion is implementation-defined; if char* and int are of different sizes, it can lose information.
Some conversions may be done explicitly, for example between different arithmetic types. This one may not.
Your compiler should complain about this. Crank up the warning levels until it does. (Tell us what compiler you're using, and we can tell you how to do that.)
EDIT :
The printf call:
printf(a);
has undefined behavior in C90, and is a constraint violation, requiring a diagnostic, in C99, because you're calling a variadic function with no visible prototype. If you want to call printf, you must have a
#include <stdio.h>
(In some circumstances, the compiler won't tell you abut this, but it's still incorrect.) And given a visible declaration, since printf's first parameter is of type char* and you're passing it an int.
And if your compiler doesn't complain about
double a="Hello World";
you should get a better compiler. That (probably) tries to convert a pointer value to type double, which doesn't make any sense at all.
"Hello World" is an array of characters ending with char '\0'
When you assign its value to an int a, you assign the address of the first character in the array to a. GCC is trying to be kind with you.
When you print it, then it goes to where a points and prints all the characters until it reaches char '\0'.
It will compile because (on a 32-bit system) int, int *, and char * all correspond to 32-bit registers -- but double is 64-bits and char is 8-bits.
When compiled, I get the following warnings:
[11:40pm][wlynch#wlynch /tmp] gcc -Wall foo.c -o foo
foo.c: In function ‘main’:
foo.c:4: warning: initialization makes integer from pointer without a cast
foo.c:5: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast
foo.c:5: warning: format not a string literal and no format arguments
foo.c:5: warning: format not a string literal and no format arguments
foo.c:6: warning: control reaches end of non-void function
As you can see from the warnings, "Hello World" is a pointer, and it is being converted to an integer automatically.
The code you've given will not always work correctly though. A pointer is sometimes larger than an int. If it is, you could get a truncated pointer, and then a very odd fault when you attempt to use that value.
It produces a warning on compilers like GCC and clang.
warning: initialization makes integer from pointer without a cast [enabled by default]
The string literal "Hello World" is a const char *, so you are assigning a pointer to an int (i.e. casting the address of the first character in the string as an int value). On my compilers, gcc 4.6.2 and clang-mac-lion, assigning the string to int, unsigned long long, or char all produce warnings, not errors.
This is not behavior to rely on, quite frankly. Not to mention, your printf(a); is also a dangerous use of printf.

Resources