When I compile the following code, Visual studio shows warning of C4477. Why this warning generated by visual studio? And How can I fix this code?
warning : warning C4477: 'printf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'int *'
#include <stdio.h>
int main(void) {
int num = 0;
int *pi = #
printf("Address of num: %d Value: %d\n", &num, num);
printf("Address of pi: %d Value: %d\n", &pi, pi);
return 0x0;
}
Because you are using incorrect format specifier. %d is to print an int. To print pointers use %p and cast to void*:
printf("Address of num: %p Value: %d\n", (void*)&num, num);
printf("Address of pi: %p Value: %p\n", (void*)&pi, (void*)pi);
The cast to void* is needed because variadic functions don't do any type conversions from type * to void * as required for %p. Quoting the standard:
7.21.6 Formatted input/output functions (C11 draft)
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.
Said another way, your argument &num (or in the form (void *) &num) is telling the compiler that you wish to pass in to the function the address of num, not the value of num. The printf function with it's %d format specifier is expected a value, not an address. The complaint is the compiler realizing this difference.
Note: in the 32 bit work, an address and an int are both 32 bits and so things (i.e. the %d) would just work. But, in the 64 bit world, an int is still 32 bits long but an address is 64 bits. I.E. you can't accurately represent the value of the pointer with a %d. That's why %p got created. It (the compiler) is smart enough to handle a %p parameter as a 32 bit quantity when compiled for 32 bits and a 64 bit quantity when compiled for 64 bits.
Read up on computer architecture, hardware stacks and their bit sizes. While you're at it you may also wish to understand the difference between little endian and big endian.
Related
What is difference between %d and %p when printing?
For example:
int main() {
int a = 9;
printf("%d\n", &a);
printf("%p\n", &a);
return 0;
}
The printf function supports different conversions for different types of arguments: %d produces a decimal representation (d for decimal) for an argument of type int, %p produces a system dependent representation of a void pointer (p for pointer)
There are multiple problems in your code:
you pass the address of an int variable, hence a type int * where printf expects an int for the %d conversion: This has undefined behavior
you pass the address of an int variable, hence a type int * where printf expects an void * for the %p conversion: This has undefined behavior
you did not include <stdio.h> and you call the function printf without a proper prototype: the prototype inferred from the call is incompatible with the actual definition, so the behavior is undefined.
The output you observe is the result of undefined behavior: it is unpredictable and potentially irreproducible: on my system, I get different output every time I run the program, even without recompiling. Undefined behavior means anything can happen.
Here is a modified version:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
int a = 9;
printf("%d\n", a);
printf("%p\n", (void *)&a);
printf("0x%"PRIxPTR"\n", (uintptr_t)&a);
printf("%p\n", (void *)NULL);
printf("0x%"PRIxPTR"\n", (uintptr_t)NULL);
return 0;
}
Output:
9
0x7fff54823844
0x7fff54823844
0x0
0x0
The last conversion takes a uintptr_t, an integer type large enough to store an integer representation of a pointer. On my system, OS/X, the %p conversion produces the same output as 0x%x for the corresponding integer type.
Will output something like:
-1807747332,
00000083943ff6fc
The format specifier converts the value of the corresponding variable to the format data type. %d converted it to a signed integer. If you use %p it simply treats the value as the memory address of a pointer and prints it in hexadecimal.
I'm reading the book The Art Of Exploitation and there is a program called memory_segments.c which just demostrante to us how the memory segments works, the heap, the stack and etc. But when I try to compile the program it seems like that the printing isn't more compatible with now. I use gcc 10.2.0 to compile my C code.
#include <stdio.h>
#include <stdlib.h>
int global_initialized_var = 5;
int main()
{
printf("global_initialized_var is at address 0x%08x\n", &global_initialized_var);
// ... more prints, removed just to make code shorter
return 0;
}
// warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’
The author of the book's output:
...
global_initialized_var is at address 0x080497ec
...
What is the alternative for 0x%08x? And why does 0x%08x no longer work?
%x prints integer values not pointers. It is Undefined Behaviour. To print pointers use printf("%p", (void *)pointer);
The integer types which should be used when converting pointers are uintptr_t or intptr_t. For differences between pointers ptrdiff_t
To print uintptr_t or intptr_t you need to use PRIdPTR PRIiPTR PRIoPTR PRIuPTR PRIxPTR PRIXPTR printf formats. Example:
uintptr_t p = SOME_VALUE;
printf("UINTPTR_T printed as hex: %" PRIxPTR "\n", p);
What is the alternative for 0x%08x?
To properly print a pointer value, use %p with void* pointer:
printf("%p\n", (void*)&global_initialized_var);
It will use some implementation-specific form of printing it, most commonly it's the same as %#x format. If you want to get a specific numeric representation of the pointer, the best standard way would be to use the biggest possible integer type:
printf("%#jx\n", (uintmax_t)(uintptr_t)&global_initialized_var);
However, usually you can/want to just cast the pointer value to an integer and print it, as usually it's only for temporary debugging purposes, just like:
// Could truncate pointer value!
printf("%#llx\n", (unsigned long long)&global_initialized_var);
printf("%#lx\n", (unsigned long)&global_initialized_var);
printf("%#x\n", (unsigned)&global_initialized_var);
Note that unsigned has at least 16 bits, long has at least 32-bits and long long has at least 64-bits. Platforms are nowadays 64-bit, pointers are 64-bit - anyway, prefer the widest available type when printing pointer value, to be portable - thus the use uintmax_t above. It was common on unix platforms to use long to cast to/from pointers - on 64-bit unix long has 64-bits, while on windows it has 32-bits.
why does 0x%08x no longer work?
It is (and was) UB due to mis-matched types.
%x expects an unsigned. &global_initialized_var is a pointer.
I'm learning about pointers, and I'm running this program from the tutorial that gives some interesting output.
#include <stdio.h>
int main () {
int var = 20; /*actual variable declaration */
int *ip; /*pointer variable declaration*/
ip = &var; /*store addres of var in ip variable*/
printf("Address of var variable: %x\n", &var);
/*address stored in ip variable*/
printf("Address of stored in ip variable: %x\n", ip);
/*access the value using pointer */
printf("value of *ip variable: %x\n", *ip);
return 0;
}
And to my understanding, the site says my output should be Two matching memory locations, and the value of 20 for the "Value of *ip variable" part. I have no issue with the memory locations, they both come up as the same just as one would assume however, the last part, its telling me the value of the *ip variable is 14?
Address of var variable: 6d73b8cc
Address of stored in ip variable: 6d73b8cc
value of *ip variable: 14
Whats going on here? Compiled with gcc, which gives one warning about format %x expects an unsigned integer but argument 2 has type of....
MorePointers.c:10:9: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("Address of var variable: %x\n", &var);
^
MorePointers.c:13:9: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("Address of stored in ip variable: %x\n", ip);
Correct me, please, if I am wrong, but could it be due to the format, which when expecting an unsigned integer could cause the number to be one other than 20? being an unsigned integer is within values 0 to 65,535 or 0 to 4,294,967,295, while an integer is within values -32,768 to 32,767 or -2,147,483,648 to 2,147,483,647?
What's happening is that you've printed the integer value 20 as a hexadecimal value, which rightly is displayed as 14. I suspect you were expecting 20 decimal. :)
Some fixes :-
printf("Address of var variable: %x\n", &var);
This is incorrect, a pointer should be printed as a pointer value
printf("Address of var variable: %p\n", &var); /* << ensure pointer value */
When you call printf, it mixes fixed text with parameters in order...
printf( "hello %d world %d today %d\n", 1,2,3 );
Prints hello 1 world 2 today 3
The way a parameter is displayed is based on the format specifier.
%s /* displays as a string */
%d /* displays as an integer in normal decimal */
%x /* displays an integer in hexa-decimal. */
%f /* displays as a floating point */
%p /* displays a pointer address */
Decimal goes 0,1,2,..,8,9,10,11,...,14,15,16,17,...,20
Hexadecimal goes 0,1,2,..,8,9, A, B,..., E, F,10,11,...,14
You are printing the hexadecimal value of var or *ip, which is 0x14.
Use %d for decimal value.
The first two printf() statements have undefined behaviour, as %x requires the corresponding parameter to be of type unsigned, not a pointer. Hence the warnings. Look up the %p format if you want to print the value of pointers (and convert the pointers to (void *) to use that).
%x prints the value in hex, base 16. The value of 20 (decimal) in hex is 14 (1*16 + 4). Strictly speaking, you should convert an int to unsigned to use %x.
printing addresses with printf() should be done with '%p' not '%x'.
If you compiled with all the warnings enabled (for gcc, at a minimum use: -Wall -Wextra -pedantic )
then the compiler would have told you, for line 10 and line 13:
warning: format: '%x' expects argument of type 'unsigned int', but argument two has type 'int*' [-Wformat=]`
The last call to printf() is passing a type of 'int' but the '%x' is expecting a unsigned int.
suggest declaring 'ip' and 'var' as unsigned int rather than int
or change the last printf() to:
printf("value of *ip variable: %d\n", *ip);
you can read.learn about the format specifiers for printf() at:
<http://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output>
or reading the printf man page
I'm a beginner in C, today I'm studying the pointer part, I found that I can print an address directly, when using the right type escaper, I can even print the intended value stored in that memory address.
Later, I did some experiments:
##### CODE PART ######
#include <stdio.h> // Define several variable types, macros, and functions about standard input/output.
int main () {
char my_string[] = "address test";
printf("%s\n", &my_string);
printf("%p\n", &my_string);
printf("%d\n", &my_string);
printf("%x\n", &my_string);
printf("\n");
char *p = "pointer string test";
printf("%s\n", p);
printf("%p\n", p);
printf("%d\n", p);
printf("\n");
char *p2 = 'p';
printf("%c\n", p2);
printf("%p\n", p2);
printf("%d\n", p2);
return 0;
}
##### OUTPUT #####
address test
0x7fff58778a7b
1484229243
58778a7b
pointer string test
0x107487f87
122191751
p
0x70
112
I'm not quite understand the behavior of %d format output at first, But after more observations and experiments. I found that %d is converting part of the hex value of the memory address.
But for address of my_string it omitted the 0x7fff part, for address of p it omitted 0x10 part, for p2 it omitted the 0x part. In my cognition, 0x is the head sign of a hex value.
But how should I know how much digits will be omitted by C when converting a memory address to int, as it does in the sample of my_string and p?
PS: My system version is OSX10.10
The C standard (ISO/IEC 9899:2011) has this to say about converting between pointers and integers:
6.3 Conversions
6.3.2.3 Pointers
¶5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.67)
¶6 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. The result need not be in the range of values of any integer type.
67) The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to
be consistent with the addressing structure of the execution environment.
Note that the behaviour of converting between pointers and integers is implementation defined, not undefined. However, unless the integer type used is uintptr_t or intptr_t (from <stdint.h> — or <inttypes.h>), you are likely to see truncation effects if the sizes of the pointer and the integer types do not match. If you move your code between 32-bit and 64-bit systems, you will run into problems somewhere.
In your code, you have 64-bit pointers (because you're on Mac OS X 10.10 and you need to specify explicitly -m32 to get a 32-bit build, but your results are consistent with a 64-bit build anyway). When you pass those pointers to printf() with the %d and %x conversion specifications, you are requesting printf() to print a 32-bit quantity, so it formats 32 of the 64 bits you passed. The behaviour is ill-defined; you aren't getting a conversion, per se, but the calling code (in main()) pushes a 64-bit pointer onto the stack and the called code (printf()) reads a 32-bit quantity off the stack. If you requested that a single call to printf() should print several values (e.g. printf("%d %x\n", p, p);), you'd get more surprising results.
You should compile with options like:
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition -Werror …
With those options, your code would not compile; the compiler would complain about mismatches between the format strings and the values passed. When I saved your code into a file noise.c and compiled it with clang (from XCode 7.2, running on Mac OS X 10.10.5), I got:
$ /usr/bin/clang -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror noise.c -o noise
noise.c:5:20: error: format specifies type 'char *' but the argument has type 'char (*)[13]'
[-Werror,-Wformat]
printf("%s\n", &my_string);
~~ ^~~~~~~~~~
noise.c:7:20: error: format specifies type 'int' but the argument has type 'char (*)[13]' [-Werror,-Wformat]
printf("%d\n", &my_string);
~~ ^~~~~~~~~~
noise.c:8:20: error: format specifies type 'unsigned int' but the argument has type 'char (*)[13]'
[-Werror,-Wformat]
printf("%x\n", &my_string);
~~ ^~~~~~~~~~
noise.c:14:20: error: format specifies type 'int' but the argument has type 'char *' [-Werror,-Wformat]
printf("%d\n", p);
~~ ^
%s
noise.c:17:11: error: incompatible integer to pointer conversion initializing 'char *' with an expression of
type 'int' [-Werror,-Wint-conversion]
char *p2 = 'p';
^ ~~~
noise.c:18:20: error: format specifies type 'int' but the argument has type 'char *' [-Werror,-Wformat]
printf("%c\n", p2);
~~ ^~
%s
noise.c:20:20: error: format specifies type 'int' but the argument has type 'char *' [-Werror,-Wformat]
printf("%d\n", p2);
~~ ^~
%s
7 errors generated.
$
Compile with stringent warnings, and pay heed to those warnings.
There is no such thing like "hex value". A number is an amount. Decimal and hex are just representations of the number using different conventions. One can, as well, represent the number using roman numerals and its value still remains the same.
The address of a variable is a concept, not a physical thing. It usually happens to be a (large) number on the current OSes and CPU architectures but this is not set in stone.
Depending on the compiler and the code it compiles, a variable can be stored in memory (it has an address that looks like a large integral number) or not. The compiler can optimize the code and store a temporary variable in a CPU register; in this case it doesn't have an address.
Back to your code. &my_string is the address of variable my_string. It looks like a number. You probably run the code on a 64-bit processor. Memory addresses in this situation are 64-bit unsigned numbers.
printf("%p\n", &my_string); - prints a 64-bit unsigned number (the most appropriate representation of a pointer on the hardware architecture you are using).
printf("%d\n", &my_string); - you pass a 64-bit number to printf() but because of the %d specifier it thinks the value is 32-bit. It grabs only half of the passed value (4 out of the 8 bytes) and represents it as a signed integer. But which half? It depends on the architecture where the code runs. The behaviour of this code is undefined.
printf("%x\n", &my_string); - similar with %d, it prints only (the same) half of the passed value using the hexadecimal notation. The behaviour of this code is, again, undefined.
The 0x prefix is not part of the hex representation; it is just a marker that signals to the C compiler that a number in the hexadecimal representation follows. While the hex representation is universal, different languages use different ways to encode them. Even the C language uses two different markers for them; 0x is used to prefix the numbers and \x is used to prefix the hex representation of a character.
There's no rule. This is not covered by the C standard. Your code causes undefined behaviour. Any results you observe for this entire program are meaningless.
With printf you must convert the arguments to the right type yourself.
printf("%d\n", &my_string);
printf("%x\n", &my_string);
is cause for undefined behavior. The format specifier and the argument type must match for printf to work correctly. For a list of valid format specifiers and data types to which they apply, take a look at http://en.cppreference.com/w/c/io/fprintf.
The following lines suffer from the same problem.
printf("%d\n", p);
printf("%c\n", p2);
printf("%d\n", p2);
The line
char *p2 = 'p';
assigns the integer value that represents the character 'p' to p2. However, that is not a valid address.
The integral types that can be used to hold a pointer are intptr_t and uintptr_t. Hence, you can use:
char my_string[] = "address test";
intptr_t ptr = &my_string;
However, you cannot use %d format specifier to print that value. You will need to use:
printf("%" SCNdPTR "\n", ptr);
to print that.
Take a look at http://en.cppreference.com/w/c/types/integer for more details.
This question already has answers here:
difference between printing a memory address using %u and %d in C?
(5 answers)
Closed 7 years ago.
I know that %d is for int, and %u for unsigned int and %p for the address.
But my question is we can print the address using %u,%d on a Windows machine but the same cannot be done on Linux.
For example:
#include<stdio.h>
int main() {
int x=15;
int *p;
p=&x;
printf("%d \n",x);
printf("%d \n",&x);
printf("%u \n",&x);
printf("%p \n",p);
printf("%x \n",p);
printf("%u \n",&p);
return 0;
}
OUTPUT:
15
2358812
2358812
000000000023FE1C
23fe1c
2358800
It is seen that the hex value(23fe1c) is changed to decimal using %d and %u.
But the exact same code gives error on Unix/Linux.
ERROR:
format (%d) expects arguments of type 'int', but argument 2 has type 'int*'
GCC has the ability to perform type checking on variadic functions, specifically using the format function attribute. This forces a check that the types passed as the variadic arguments exactly match the format string supplied.
GCC complains because the data types used in the printf format string and the passed parameters are not compatible. The portable and standard way to print a pointer is by using the %p specifier. However, you can find other places where people may use %x for the same purpose. As others have said, using %u and %d are not compatible with a pointer data type. They may work on some compilers like in the Windows case but other stricter compiler (like GCC in this case) could complain about the datatype being passed since int is not the same as int*. In order to use the %p notation you have to cast the pointer to void, as from the C99 standard:
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.
That's said, the %p is implementation defined so the output could change from an implementation to the other. Any way using %d or %u to print a pointer in general is not a good idea. Depending on the implementation a pointer could be a simple int or a long int. So you could get an undefined behavior if you try to print a pointer by using a %d or %u on a machine where the addresses are stored in long int variables.
If you would like to control the output then another option is to use uintptr_t provided by #include <inttypes.h>.