5

I was playing with pointers in order to fully get the concept and then wanted to subtract two pointers expecting the distance between these two addresses or something, but apparently I was wrong, so here is my code.

int x = 5, y = 7;
int *p = &y;
int *q = &x;
printf("p is %d\nq is %d\np - q is %d", p, q, (p - q));

Why does the program output p - q is 1? Thank you.

MD XF
  • 7,860
  • 7
  • 40
  • 71
Kamoukou
  • 123
  • 1
  • 2
  • 6
  • 8
    So what did you expect and why? – Eugene Sh. Oct 11 '16 at 19:06
  • 1
    If `x` and `y` are stored next to each other, they will be one `int` size apart, so the difference is 1 ... in that specific case. But pointer diffs don't make sense unless you're talking about an array - ie a consecutive block of elements. – John D Oct 11 '16 at 19:09

7 Answers7

19

It is undefined behavior. According to the standard (N1570):

6.5.6 Additive operators
....
9 When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements.

Note that when allowed, the result is the subscripts difference. So if pointers point to two sequential elements of the same type, the subtraction gives 1, regardless of the size of the type. (This is perhaps the reason why you get 1 in your concrete case.)

AlexD
  • 32,156
  • 3
  • 71
  • 65
  • 1
    Actually, it's not 100% clear that the behavior is undefined if `x` and `y` happen to be adjacent in memory. The standard acknowledges that possibility, though it explicitly defines the behavior only for an equality comparison between a pointer past the end of one object and a pointer to an adjacent object. See [N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.5.9 paragraphs 6-7. For subtraction, you should just assume the behavior is undefined. – Keith Thompson Oct 11 '16 at 19:11
  • 4
    Well, the point that it is not guaranteed that these locations will be adjacent in the memory, so it is implementation defined at the very least. – Eugene Sh. Oct 11 '16 at 19:14
5

Your particular case is cause for undefined behavior since p and q point to unrelated objects.

You can make sense of p-q only if p and q point to the same array/one past the last element of the same array.

int array[10];
int* p = &array[0];
int* q = &array[5];

ptrdiff_t diff1 = q - p;  // Valid. diff1 is 5
ptrdiff_t diff2 = p - q;  // Valid. diff2 is -5

q - p is 5 in this case since they point to elements of the array that are 5 elements apart.

Put another way, p+5 is equal to q. If you start from p and step over 5 elements of the array, you will point to the same element of the array that q points to.


As an aside, don't use the format specifier %d for printing pointers. Use %p. Use %td for ptrdiff_t.

printf(" p is %p\n q is %p\n p-q is :%td", p, q, p-q);`
//            ^^        ^^

See http://en.cppreference.com/w/c/io/fprintf for the valid format specifiers for the different types.

Jivan Pal
  • 182
  • 10
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 4
    You should use type `ptrdiff_t` for pointer subtraction. As for printing a pointer value, shouldn't the argument be `(void*)p`? – Weather Vane Oct 11 '16 at 19:28
  • @WeatherVane In general, when a function has an argument that's a pointer-type, an implicit cast may occur, depending on the compiler settings; the C89/C90 standard (which introduced `void*`) forbids implicit pointer casts, but e.g. GCC will permit it if you specify `-Wno-incompatible-pointer-types`. – Jivan Pal Aug 30 '21 at 15:21
  • @WeatherVane For variadic functions like `printf()`, such a check can't be done at compile-time; the implementation of `printf()` simply *assumes* that the argument corresponding to a `%p` parameter is of type `void*`. Thankfully, in practice, the binary data stored under the hood in a pointer `p *x` and any corresponding pointer of another type, `q *y = (q*)x`, is identical, though I don't know if this behaviour is defined in any C standard. The only related behaviour defined in any standard that I'm aware of is that `y == x` must evaluate to "true". – Jivan Pal Aug 30 '21 at 15:22
  • @WeatherVane In particular, C99 §6.3.2.3(2) says: For any qualifier *q*, a pointer to a non-*q*-qualified type may be converted to a pointer to the *q*-qualified version of the type; the values stored in the original and converted pointers shall compare equal. – Jivan Pal Aug 30 '21 at 15:25
  • @JivanPal I don't know what doubts you are raising with my comment. Please see [this](https://stackoverflow.com/questions/54598093/why-is-a-pointer-cast-as-voidp-when-used-in-printf) and others, and the comment under the accepted answer. – Weather Vane Aug 30 '21 at 15:58
  • @WeatherVane I am saying that, in practice, there is no need to explicitly cast to `void*` in this scenario. Thanks for the linked question, the comment you mentioned and [one other comment there](/questions/54598093/#comment95992924_54598093) seem to point out that this is standardised behaviour, at least as of C11. – Jivan Pal Aug 30 '21 at 16:11
  • @JivanPal in practice, one *should* cast it, because unlike small integer and `float` types, there is no automatic promotion to a variadic function. It is, after all, at worst harmless. – Weather Vane Aug 30 '21 at 16:12
  • @WeatherVane, agreed, as a matter of good practice, one *may/should*, but as a matter of what is strictly necessary, one *needn't*, and there is never any opportunity for confusion in the case of `%p`. – Jivan Pal Aug 30 '21 at 16:29
  • @WeatherVane ***"because unlike small integer and float types, there is no automatic promotion to a variadic function."*** — I am not sure what you mean to say here. There is no automatic promotion/casting in variadic functions at all, regardless of whether the argument is `int`, `float`, `int*`, `void*`, etc.. If I do `float x = 9.7; printf("%d", x);`, the behaviour is implementation-defined. If I do `float x = 9.7; printf("%p", &x);`, the behaviour is *guaranteed* to be the same as `float x = 9.7; printf("%p", (void*)(&x));` – Jivan Pal Aug 30 '21 at 16:30
  • @JivanPal there is: integer arguments smaller than `int` are promoted to `int` and `float` is promoted to `double` automatically. This has nothing to do with the format specifiers, and in this case `printf`'s `%p` *expects* a pointer to `void` – Weather Vane Aug 30 '21 at 17:28
  • @WeatherVane, promotion is only relevant if the function's type signature defines the argument type, which is not the case for the vector arguments of a variadic function. We've been talking about variadic functions from the outset, so I don't understand why you think promotion is relevant here. The only point I'm making re `printf()` is that yes, its documentation says that `%p` relies on the corresponding argument being `void*`, but pointer representations *must all be equivalent*, so for all intents and purposes it just relies on that argument being *any pointer*, not necessarily `void*`. – Jivan Pal Aug 30 '21 at 19:20
  • @JivanPal I think you are confusing *type conversion* with *type promotion*. – Weather Vane Aug 30 '21 at 19:22
  • @WeatherVane Do you mean to say that if I have a function `f(short x, ...)`, and I do `short x = 7; float y = 1.5; f(x,y)`, then `f()` really has an `int` and a `double` in its scope? – Jivan Pal Aug 30 '21 at 19:25
  • I am talking about `printf` where the type of (or number of) arguments is not specified. And pointers are not guaranteed to be the same size: please see this [answer](https://stackoverflow.com/a/1241314/4142924). Casting to `void*` makes the code portable. – Weather Vane Aug 30 '21 at 19:27
  • 1
    @JivanPal please see [better understanding type promotion of variadic parameters in c](https://stackoverflow.com/questions/44421125/better-understanding-type-promotion-of-variadic-parameters-in-c) – Weather Vane Aug 30 '21 at 19:33
  • @WeatherVane Thanks, that cleared things up. I pulled out my copy of K&R 2nd ed., and this isn't mentioned in the brief section on variadic functions, though it is hinted at in the section on type conversion and in the appendix, but only in reference to "old-style" function prototypes (or lack thereof), rather than variadic functions specifically (for which I suppose you could say that the argument vector `...` lacks a prototype and therefore obeys the same rules). – Jivan Pal Aug 30 '21 at 19:58
  • 1
    @WeatherVane, I also seem to have missed the following sentence from the standard: "Pointers to other types need not have the same representation or alignment requirements." – Jivan Pal Aug 30 '21 at 19:58
1

Pointer arithmetic works like that. It doesn't give you differences between two addresses. Instead it will show difference between two variables as if they are stored in an array. so, no matter if your variables (of same type ) are 4 bytes, 8 bytes or 1 byte, if stored in adjacent memory location their pointer subtraction will always result in 1 or -1.

1

The subtraction of 2 pointers give the distance in between the 2 variables. For eg.

//let the address of a is 1000 then of a+1 will be 1004

int a[]={1,2,3};
int *p1=a;
int *p2=a+1;
printf("%u",p2-p1);

The result of this will be 1 not 4. Same here in your case the the location of x and y are consecutive that is why the ans. is 1.

1

The formula used by pointer substraction is:

( p2 - p1 ) == ( addr( p2 ) - addr( p1 ) ) / sizeof( T )

with T being the type of both p1 and p2.

int array[10];
int* p1 = array + 2;
int* p2 = array + 5;

int* a = p2 - p1; // == 3
int* b = p1 - p2; // == -3 
xy2
  • 6,040
  • 3
  • 14
  • 34
0
int a=5,b=6;
int *ptr=&a,*diff;
diff=ptr;
printf("%p\n",diff);
ptr++;ptr++;ptr++;ptr++;ptr++;ptr++;ptr++;ptr++;ptr++;ptr++;
printf("%p\n",ptr);

printf("diff==%td\n",(ptr-diff)*sizeof(int));

(diff-ptr) will provide the distance between two variables, but will not provide the memory gap between two pointers. Important: only the same type of pointer can be subtracted.

-1

The subtraction of two pointers in array will give the distance between the two elements.

Let the address of first element i.e., is 1000 then address of second element a+1 will be 1004. Hence p1 = 1000 and p2 =1004.

p2-p1 = (1004- 1000) /size of int = (1004-1000)/4 =4/4 =1
RamPrakash
  • 1,687
  • 3
  • 20
  • 25