0

I am just studying for an exam and the following question is asked, what output does the following program generate in 32 bit or 64 bit?

#include <stdio.h>
int main(int argc, char **argv)
{
 long *p = (long *)8;
 short *q = (short *)0;
 int c, i, l;

 p = p + 1;
 q = q + 1;

 printf("p = %p q = %p\n", p, q);

 c = (char *)p - (char *)q;
 i = (int *)p - (int *)q;
 l = (long *)p - (long *)q;

 printf("c = %d, i = %d, l = %d\n", c, i, l);

 return 0;
}

The result on 32bit is the following:

p = 0xc q = 0x2
c = 10, i = 2, l = 2

The result on 64bit is the following:

p = 0x10 q = 0x2
c = 14, i = 3, l = 1

Only I must confess, I can't understand these results at all, because when I count up a pointer it should point to something random in the memory, it always comes out 0xc or 0x10.

Can someone help me?

Joe Daniel
  • 41
  • 1
  • 7
  • 1
    If you add `1` to a pointer it adds the size of the type to the value (address) held by the pointer variable. So when you added 1 to `short *q` its value increased by 2. Note that `c = (char *)p - (char *)q;` is *undefined behaviour* because it is pointer arithmetic between different objects. – Weather Vane Jul 13 '21 at 18:24
  • Do you understand hexadecimal numbers? – user3386109 Jul 13 '21 at 18:25
  • yes now it makes more sense. so i add 8 + 4(32 bit long) Byte = 12 0xc and 8+8(64bit long)Byte = 16 0x10. Makes sense now – Joe Daniel Jul 13 '21 at 18:27
  • but the secound print. When i cast my 12 Byte long in short it it a 12x 1Byte Shots right? subtraction 1 short Byte is 11 but the Result is 10 ... That makes no sense or i am missing out something? – Joe Daniel Jul 13 '21 at 18:28
  • *it should point to something random in the memory* - but `%p` is not printing the value of the pointer's target, but the pointer itself. None of the statements dereference a pointer. After assigning them those hard coded values, you'd be in trouble anyway. – Weather Vane Jul 13 '21 at 18:28
  • This whole program is a bunch of UB :( It's really frustrating it is coming from a source that is supposed to teach C. – Eugene Sh. Jul 13 '21 at 18:30
  • Note too that `sizeof(long)` isn't dependant on whether you have a 32 or 64 bit system, which usually determines the size of a *pointer*. – Weather Vane Jul 13 '21 at 18:34
  • sizeof(long) should be 32 on 32bit and 64 on 64bit Unix System no? – Joe Daniel Jul 13 '21 at 18:36
  • Please see [What is the bit size of long on 64-bit Windows?](https://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows) The `sizeof(long)` is minimum 32 bits and is implementation defined. – Weather Vane Jul 13 '21 at 18:38
  • yeah i remember thaths the reason for long long – Joe Daniel Jul 13 '21 at 18:40

2 Answers2

2

Only I must confess, I can't understand these results at all, because when I count up a pointer it should point to something random in the memory, it always comes out 0xc or 0x10.

The program doesn't access "value in memory pointed to by the pointer" for any of the pointers. It only does pointer arithmetic (changing what the pointer points to).

The goal of the question is likely to test if you understand how pointer arithmetic works in C.

How pointer arithmetic works in C is that it hides a "multiplication by sizeof(whatever the pointer points to)". In other words, p = p + n; is roughly equivalent to p = (char *)p + n * sizeof(*p);, or alternatively p = &p[n];.

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • Yes thanks, taht helped me, but how is the behavior when Casting around these Pointers in the Substraction? c = (char *)p - (char *)q; – Joe Daniel Jul 13 '21 at 18:37
  • @JoeDaniel: The cast to `(char *)` effectively disables the "multiply by sizeof()" behavior (by turning it into "multiply by 1" which does nothing, because `sizeof(char)` is 1). It also prevents the compiler from complaining about subtraction of different types. – Brendan Jul 13 '21 at 18:42
  • I played now around a bit so c makes sense its bascily a 12-2 = 10 which is right But i = (int *)p - (int *)q; is 2 (32bit), how is that generated ? – Joe Daniel Jul 13 '21 at 18:46
1

First, I should point out that there's a lot of undefined behavior in this code, and it makes a lot of assumptions that aren't guaranteed by the standard. But that having been said, the question can be answered in terms of what you'll see on a typical implementation.

Let's assume that the data type sizes (in bytes) on the two machines are:

type 32-bit size 64-bit size
long 4 8
int 4 4
short 2 2
char 1 1

Again, I don't think these are guaranteed, but they are very typical.

Now consider what the code is doing. First, it's setting

   long *p = (long *)8;
   short *q = (short *)0;

These addresses do not refer to valid data (at least, not portably), but the code is never actually deferencing them. All it's doing is using them to perform pointer arithmetic.

First, it increments them:

   p = p + 1;
   q = q + 1;

When adding an integer to a pointer, the integer is scaled by the size of the target data type. So in the case of p, the scale is 4 (for 32-bit) or 8 (for 64-bit). In the case of q, the scale is 2 (in both cases).

So p becomes 12 (for 32-bit) or 16 (for 64-bit), and q becomes 2 (in both cases). When printed in hex, 12 is 0xc and 16 is 0x10, so this is consistent with what you saw.

Then it takes the differences between the two pointer, after first casting them to various different pointer types:

 c = (char *)p - (char *)q;
 i = (int *)p - (int *)q;
 l = (long *)p - (long *)q;

These results exhibit undefined behavior, but if you assume that all pointer values are byte addresses, then here's what's happening.

First, when subtracting two pointers of the same type, the difference is divided by the size of the target data type. This gives the number of elements of that type separating the two pointers.

So in the case of c (type char *), it divides by 1, giving the raw pointer difference, which is (12 - 2) = 10 (for 32-bit) and (16 - 2) = 14 (for 64-bit).

For i (type int *), it divides by 4 (and truncates the result), so the difference is 10 / 4 = 2 (for 32-bit) and 14 / 4 = 3 (for 64-bit).

Finally, for l (type long*), it divides by 4 (for 32-bit) or 8 (for 64-bit), again truncating the result. So the difference is 10 / 4 = 2 (for 32-bit) and 14 / 8 = 1 (for 64-bit).

Again, the C standard does not guarantee any of this, so it is not safe to assume these results will be obtained on different platforms.

Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
  • First of All, Big Thanks for your Answer, it makes everything clearer now. :D – Joe Daniel Jul 13 '21 at 18:52
  • I have a addtional Question, what happens when I take a Pointer from a Pointer like char **p = (char **)5; p+=2 ? when i print it, it is 0xd (13) but shouldn't be 7 like when i am doing without secound * ? – Joe Daniel Jul 13 '21 at 22:34
  • @JoeDaniel It again scales it by the size of the pointer target. In this case, the pointer has type `char **`, so the target has type `char *`, i.e. the target is itself a pointer. So the 2 is scaled by `sizeof(char *)`. Again, this is system-dependent. On a system with 4-byte pointers, you would get 5 + 2*4 = 13. But again, this is undefined behavior, and you're doing arithmetic on an unaligned pointer, so there are no guarantees about any of this. – Tom Karzes Jul 13 '21 at 22:56