1
#include <stdio.h>

int main()
{
  int a = 10;

  printf("address = %d\n ", &a);

  return 0; 
}
test.c:11:29: warning: format specifies type 'int' but the argument has type 'int *' [-Wformat]
  printf("address = %d\n ", &a);

output if I use %d

address = -376933160

output if I use %p, this is also weird, I think I should get a positive integer instead of this?

address = 0x7ffee07004d8

I know the correct way to do this should be %p, but I see in YouTube video, people can just use %d and don't get any problems. I wonder if my setting is wrong, I am using VS code to run the program.

video example

Update

Now I am aware -376933160 is an incorrect value, but still I wonder why it just outputs a random number instead of stopping the execution ?

David
  • 373
  • 3
  • 21
  • 3
    It's always wrong to use `%d` to print a pointer, as the warning tells you. The people in those youtube videos just ignore the warning – UnholySheep Sep 23 '21 at 18:46
  • But why would you? What does `-376933160` as address of memory help you? – Jorengarenar Sep 23 '21 at 18:46
  • compilers generally have specific type-check for printf arguments – Jaffa Sep 23 '21 at 18:46
  • 1
    Your compiler is correct, and whichever numpty on youtube you saw doing this is just wrong. Ignore them and get a book. – Useless Sep 23 '21 at 18:46
  • 3
    If the pointers are 32-bit (and the same as `int`) then they "get away with it". But a signed decimal address isn't very *useful*. – Weather Vane Sep 23 '21 at 18:47
  • Thanks for the prompt response , so 0x7ffee07004d8 is the correct address , but does -376933160 mean garbage value ? – David Sep 23 '21 at 18:49
  • If your pointers are 64-bit, then `-376933160` from `%d` is incorrect. If they are 32 bit, it's just not very useful. BTW `0x7ffee07004d8` *is* a positive integer. If your program output `0x7ffee07004d8` with `%p` then your pointers are *not* 32-bit. – Weather Vane Sep 23 '21 at 18:50
  • It is not a random value. Look at my answer, search the two's complement method online for a full explanation. – Lior Levin Sep 23 '21 at 19:00

5 Answers5

3

Format string identifiers have specific purposes. The %d identifier is for integer values, like short, int etc. long has %ld more variants as such.

You have a pointer, that holds an address. Although it is a numerical value, it's special in its purpose and should be formatted as %p, the proper way to print pointers.

Also, the size of pointers may change by architecture, so it may not be the same size as the %d identifier expects.

Regarding the different values that were printed to the screen: If you were to print the address of a variable in these two formats in the same execution, you may get again one 'positive' hex value and one 'negative' integer value. But, these values are actually the same. Almost. The integer value representation of the variable is only the lower 32 bits of the 64 bit value, and it's negative because it is signed, and as a pointer representation (since it's an address and sign doesn't matter) it is unsigned hex value and looks positive, though both are the same value in memory (At least the 32 bits that are equal). This is happening because of different width of variable and something called "Two's complement". You can further read about it here Note: The two values you mentioned are not the same, since you got them in different executions of the program and ASLR was on, the actual address value of a has changed between executions.

It is important to mention, even though I refer to pointers in this answer as numerical values, it is not correct to regard them as so, as they hold addresses which are their own type category (Thanks @JohnBullinger for clarifying).

Use the correct format identifier to avoid this warning, at it is informing you that you may have miss typed or used the wrong variable since it is not a regular numerical value you're trying to print, but an address.

Lior Levin
  • 179
  • 1
  • 13
  • 2
    It is best not to assert that addresses are integers. They can be *converted to* integers, but C does not depend on there being a 1:1 correspondance, nor on the representations of addresses to be the same as the representations of any corresponding integers. – John Bollinger Sep 23 '21 at 19:01
  • @JohnBollinger exactly right. My point was that they are numerical, miss used the term integer. Fixed now. – Lior Levin Sep 23 '21 at 19:03
  • Re “Although it is a numerical value”: [Pointers are not necessarily implemented as numerical values.](https://stackoverflow.com/a/11714314/298225) – Eric Postpischil Sep 23 '21 at 19:05
  • You can [edit] your answer, don't add in a comment. -- You could further enhance your answer, if you show that apparently the OP's system has 32 bit integers and 64 bit pointers, and it seems to be little endian. The lower 32 bits of 0x7ffee07004d8 are 0xe07004d8, and this is -376933160 in signed decimal. – the busybee Sep 23 '21 at 19:05
  • No, my point is that they are *not* numerical, integer or otherwise. Or at least it is best not to regard them that way. Addresses are addresses, which is its own type category. – John Bollinger Sep 23 '21 at 19:05
  • "The %d identifier is for integer values, like short, int, long" nah... not long. – Support Ukraine Sep 23 '21 at 19:09
  • @ChrisDodd Noted and revised. – Lior Levin Sep 23 '21 at 19:31
  • 1
    Not only are −376933160 and 0x7ffee07004d8 not the same, the former, in two’s complement, is not the low 32 bits of the latter. It is 0xe98874d8. These are different values that came from different addresses in different executions of the program. Address space layout randomization caused `&a` to be different in different executions. – Eric Postpischil Sep 23 '21 at 19:32
  • @EricPostpischil the two's complement and the width difference are two different explanations. the two's complement about the negativity vs positivity and the width difference about the drastic value change. – Lior Levin Sep 23 '21 at 19:35
  • 1
    @LiorLevin: These are **not** values derived from the same address, regardless of whether two’s complement, negativity, and/or width are taken into consideration. They do **not** have the same low 32 bits regardless of these factors. They do not even have the same low 16 bits. E.g., if you take 0x7ffee07004d8, take its low 32 bits, and interpret it as a two’s complement representation, you get −529529640, not − 376933160. – Eric Postpischil Sep 23 '21 at 19:36
  • @EricPostpischil Totally correct. Didn't check the values. Will fix my answer. – Lior Levin Sep 23 '21 at 19:38
2

Now I am aware -376933160 is an incorrect value, but still I wonder why it just outputs a random number instead of stopping the execution ?

Because it is Undefined Behaviour. It does what it does. No rules.

Using the wrong format specifier is cheating the compiler. Imagine that I want to buy your old car. The price is $5000. I pay you but not in US$. If I pay £5000 you are the winner. But if I pay in drachmas 5000GDR you will not be very happy. And your behaviour will be undefined. Maybe you will chase me with the baseball bat in your hand or maybe you simply give up and accept your losses.

Same happens with the compiler and the printf function.

0___________
  • 60,014
  • 4
  • 34
  • 74
  • +1 for the currency analogy, great way to explain the importance of using the correct format specifiers, I may steal it... P.S.: small bug, ISO 4217 code for drachma is GRD, not GDR. – Luca Polito Sep 23 '21 at 20:56
2

For the same reason you can't use %f, or %c, or anything other than %p.

%d expects its corresponding argument to have type int; if the argument doesn't have type int, then the behavior is undefined. You may get reasonable-looking output, or you may not.

On most 64-bit machines sizeof (int *) > sizeof (int). On x86 pointers are 64 bits wide while ints are 32 bits wide. If you pass a pointer as the argument for %d, printf will only pull half of the pointer value - the output is gibberish.

Now I am aware -376933160 is an incorrect value, but still I wonder why it just outputs a random number instead of stopping the execution ?

Again, you're likely only seeing the lower 32 bits of a 64-bit pointer value.

Undefined behavior does not mean "stop execution" or "print an error" or anything else - it just means that neither the compiler nor the runtime environment are required to handle the situation in any particular way.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

The loss of bits and the apppearence of the minus sign provoke a warning when you do it directly:

adr.c:7:13: warning: overflow in conversion from 'long int' to 'int' changes value from '140732663858392' to '-529529640' [-Woverflow]
    7 |   int adr = 0x7ffee07004d8;

e07004d8 (lower 32 bits) is over 2^32. With 0x7ffe0000000a it converts to '10'.


Not a random number, and no reason to "stop execution".

Such a "cast" rarely makes sense, especially not on pointers. (Unless you take a pointer to convert it to a long just to play with it).

-2

The compiler will generally issue a warning if there is a conversion between pointer and integer types without an explicit cast as a protection to the programmer. You can eliminate the warning by casting &a to long long int.

Depending on the system you may be able to print the decimal value if you cast it with %lld:

printf("address = %lld\n ", (long long int)&a);

However, not all systems may support ll as a valid length.

Jonathon S.
  • 1,928
  • 1
  • 12
  • 18
  • totally depends on the computer you use; as such, his compiler said the given type was `int` instead of `int*`, so it's not the length that's the issue – Jaffa Sep 23 '21 at 18:54
  • 2
    The format specifier for `long long` is `%lld`. Think about it: if it were `%dL` how would `printf` know whether the `L` is modifying `%d` or is to be output as the letter `L`? The `L` is used as a suffix to a constant value, for example `123456789LL`. – Weather Vane Sep 23 '21 at 18:55
  • @Geoffroy A pointer (`&a`) is being used in the `printf` expression, not an `int`. – Jonathon S. Sep 23 '21 at 18:58
  • 1
    There are more kinds of pointers than "integers". The system I'm working at currently uses a 16 bit segment number and a 32 bit "offset". It makes no sense to show this as an integer, especially in decimal. – the busybee Sep 23 '21 at 18:59
  • @thebusybee Agree. I would expect hex would make more sense. – Jonathon S. Sep 23 '21 at 19:07
  • @WeatherVane Noted. Revised. – Jonathon S. Sep 23 '21 at 19:08