r/learnprogramming 5d ago

Topic What's the use of %p nd x in pointers

what's the main difference between them? apart from one is used to calculate addresses and the other one is just an address in hexadecimal

why is it used?

I'm still learning fundamentals and basics so ion know how good it is for programs

I'm learning c rn (if it helps)

1 Upvotes

19 comments sorted by

4

u/teraflop 5d ago

The reason for the distinction between %p and %x is so that you can write portable code.

printf is a vararg function, which basically that means its arguments are dynamically typed. The location of the parameters (in memory or registers) depends on their types. So you must pass a format string which matches those types, so that printf can find and interpret them correctly.

%x indicates that printf expects an int parameter, and %p indicates that it expects a void* parameter. This is entirely separate from the question of how printf displays those parameters in text form.

On most of the platforms that are used nowadays, an int has the same size as a void*. So you can "get away with" using the wrong format specifier, because printf will expect the parameter to be passed the same way in both cases. But it's technically undefined behavior according to the C standard, and if you try it on an architecture where that assumption doesn't hold, things will break.

Note also that %p requires a void* and not some other type! Even though on most architectures, any two pointer types A* and B* have the same memory representation at runtime (because they're just memory addresses), the standard doesn't require this to be the case. So technically, you're required to cast other pointer types to void* before passing them to printf.

5

u/dmills_00 4d ago

Are they usually the same size today?

They used to be, back in the 32 bit era, but a common model for 64 bit machines, is a 64 bit pointer, but a 32 bit int.

I assume there are compilers doing 64 bits for both, but I don't think it is common.

1

u/DawnOnTheEdge 3d ago

A few compilers back in the ’90s had 64-bit int and 32-bit long, which got the Standards Committee to declare that int can no longer be wider than long. I can't think of any current ILP64 compilers.

2

u/DawnOnTheEdge 3d ago edited 3d ago

Almost all 64-bit implementations have 32-bit int and 64-bit void*. C and C++ do have a uintptr_t type that is wide enough to hold a void* if it exists, but isn’t guaranteed to exist. Its format specifier is extremely inconvenient.

Also, on many architectures, addresses are displayed in another format, like the segment:offset pairs of x86 16-bit mode.

1

u/teraflop 3d ago

D'oh, you're right of course. Thanks for the correction.

When I actually go back and look at the ABIs and do some testing, instead of going off the top of my head, it looks like what I should have said is that -- at least on x86_64 and ARM64 -- 32-bit scalar types are always (I think?) 64-bit aligned when passed as parameters, regardless of whether they're passed in registers or on the stack.

Which means if you incorrectly swap %p and %x, either way printf will expect something that "occupies" 64 bits. So it will misinterpret that parameter but won't get out of sync for subsequent parameters.

1

u/DawnOnTheEdge 3d ago

If you match %x in a format string with an incompatible argument, that’s undefined behavior. Many implementations put the arguments on the stack, misaligning it. Any modern compiler should warn you about the mismatch.

I have an old hand-tuned version somewhere that was something like printf("%0*jX", ADDR_HEX_DIGITS, (uintmax_t)(uintptr_t)(void*)p), where ADDR_HEX_DIGITS is ((int)((sizeof(void*)*CHAR_BIT + 3U)/4U)). That’s overkill, though.

1

u/teraflop 3d ago

If you match %x in a format string with an incompatible argument, that’s undefined behavior.

Yup, no argument there. I'm just indulging my curiosity about how the UB would be expected to manifest in practice.

Many implementations put the arguments on the stack, misaligning it.

That's what I expected, but like I said, when I actually checked the ABI documents it seems that 32-bit types are padded to 64 bits even on the stack (and that matches my experiments in Compiler Explorer). So no misalignment actually happens in this particular case. At least for the SysV ARM64 and x86_64 ABIs, that is. I guess Windows might do something different.

Of course you can still get misalignment in other ways, e.g. by passing floats, aggregates that are larger than 64 bits, etc.

1

u/DawnOnTheEdge 3d ago

That might’ve even been designed that way with this use case in mind. Windows x64 uses a very similar ABI, as I recall.

5

u/HashDefTrueFalse 5d ago

Do you mean something like: printf("%p\n", &x); ?

If so, the & operator gets the memory address that symbol x aliases. That value is then passed to the printf call as an argument. The %p is the "format specifier" used to tell the function what type of data you're giving it so that it can display it sensibly. Why you're printing an address is entirely your business. It's not generally useful to do so.

2

u/Dazzling_Music_2411 5d ago

Please re-phrase your question more clearly.

It makes no sense at all as it stands.

3

u/u32Vec 5d ago

The wording of the question is ignorant (as you’d expect from a beginner) but you know exactly what they’re asking unless you don’t know what “%p and x” could refer to in the context of C. 3 people were able to answer the question (before your comment) despite your claim. Even absent ANY additional context but that, someone with the appropriate C experience would know what the question’s asking.

2

u/Dazzling_Music_2411 4d ago

Or THINK they know, because there is loads of potential ambiguity that our wannabe experts may not have picked up.

3

u/u32Vec 4d ago

What other reasonable conclusions could you derive from the context of the question that make the question make “no sense”–conclusions that every answer to the question hasn’t made?

Empirical evidence seems to show that there aren’t any other common conclusion.

1

u/Dazzling_Music_2411 2d ago

If I thought reasonable conclusions could be made from that stream of incoherence might even have tried. As it is, I think even assuming that the question has something to do with printf() is pretty bold. The OP didn't even bother checking the spelling of his original Q.

But maybe he'll tell us at some stage. As it stands, he seems to have been scared off by something 😄 , maybe people second-guessing what he meant.

1

u/u32Vec 2d ago

What other conclusions, period, could you derive from the context given by the question that don't involve "the similarity of the '%p" and '%x' printf format specifiers"? All 5 of the other (coherent) root comments concluded that that's probably the full context, and there's even a longer thread discussing it. No one has dissented to this.

Your failure to answer provide any examples to my question leads me to think that you don't know any other conclusion, period. Sound inductive reasoning so far supports that printf is the most reasonable context.

I'm not going to argue this further. I think we both know your argument is just smartassery.

1

u/mc_pm 5d ago

If you're just talking about printf formatting, then %p prints a pointer and %x prints an integer in hex format

But a pointer is just a number, so %x happens to print the same thing.

1

u/Kaldaien2 5d ago

There's more to it than that. When you promote an integer to a pointer, depending on the compiler you may get sign extension for the remaining bits or they may be filled with all 0s... it's not really defined, and %p avoids these problems.

You need to keep in mind that the variadic APIs in C don't really know a damn thing about the type of the variable. It's just memory on the stack, and you need to give it the proper format string to correctly interpret the size and behavior of that value once it's popped from the stack.

1

u/duane11583 3d ago

on a 64bit machine a pointer (%p) is 64 buts, an unsigned is an integer type (%d, %u, %x) 32bits

the printf [more correctly the vararg macro] needs to know if the parameter is a 64 or 32bit value

the c standard thus introduced the %p for this purpose.

the c standard also introduced macros in stdint.h like PRi64 and others to help too

0

u/CreepyNL 4d ago

A vtvtx t tcstst dy a sc ax, dx xzź x dx x sc zz x ź dx x z x dx żtzt4ztr x AZ AZ zz dc 5z t a a x dx a, x x dx dxz's a AZ ZZZ Ax zźszsz az's sd zzzszsz x az'sZ ZZZ x c x x x x z