2

I am unable to get that why in this program 2 - 4 gives -1, it has assigned int values to pointers rather than addresses, I know but while I compiled it compiler gave some warnings but compiled the program and it executed but...

Program

#include<stdio.h>

int main(void) {

    int *p, *q;

    int arr[] = {1,2,3,4};

    // I know p and q are pointers and address should be assigned to them
    // but look at output, why it evaluates (p-q) to -1 while p as 2 and q as 4

    p = arr[1];
    q = arr[3];

    printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);

    return 0;
}

It gives

P-Q: -1, P: 2, Q: 4
alk
  • 66,653
  • 10
  • 83
  • 219
anonymous
  • 448
  • 2
  • 9
  • What output did you expect? – Jabberwocky Jun 27 '18 at 06:47
  • 1
    `p = arr[1];` and so are implementation defined behaviour. – Sourav Ghosh Jun 27 '18 at 06:49
  • 5
    Do you intentionally define `int *p, *q;` instead of `int p, q;`? Then you should explain more about your thoughts. – Yunnosch Jun 27 '18 at 06:50
  • 1
    @Jabberwocky I expect -2, maybe I am wrong, that's why I am asking to know that what is actually happening under the hood – anonymous Jun 27 '18 at 06:54
  • 1
    @Yunnosch yes intentionally, just having fun with C – anonymous Jun 27 '18 at 06:55
  • 1
    Complement to the [duplicate](https://stackoverflow.com/questions/11713929/c-c-pointer-arithmetic): in your case `q` contains 2 and `p` contains 4. As mentioned in the duplicate: _pointer subtraction yields the number of array elements between two pointers of the same type_. Assuming `sizeof int` is 4 on your platform the difference between the pointer 4 and 2 is less then `sizeof int`, therefore the substraction doesn't really make sense. – Jabberwocky Jun 27 '18 at 07:01
  • p and q are two different pointers so any operations between them cannot be clearly defined – Prajval M Jun 27 '18 at 07:06
  • 1
    [pointers must be printed using `%p`](https://stackoverflow.com/q/9053658/995714) and [their differences like `p - q` must be printed with `%td`](https://stackoverflow.com/q/586928/995714). You're having a lot of UB there – phuclv Jun 27 '18 at 07:08
  • @phuclv why `%td` for pointer difference? I thought `%ld` would suffice, since the warning suggests so (check my answer, and let's improve it, if it has to). – gsamaras Jun 27 '18 at 07:15
  • @gsamaras technically [using the wrong format specifier is UB](https://stackoverflow.com/q/16864552/995714). In this case if the pointer is 64 bits then printing them with %d is not OK. Same with their difference which is also a 64-bit type ([`ptrdiff_t`](https://stackoverflow.com/q/7954439/995714)) – phuclv Jun 27 '18 at 07:19
  • I agree that `%d` is wrong. But as a fix I proposed `%ld`, for `long int`. I wonder whether your fix is better than mine @phuclv. – gsamaras Jun 27 '18 at 07:21
  • 1
    @gsamaras no, using the wrong format specifier is always UB. A long int may have the same size as int, like on 64-bit Windows. Or you'll need to cast to long int before using %ld. `ptrdiff_t` is not necessarily `long int` – phuclv Jun 27 '18 at 07:23
  • 1
    @phuclv you are right, thank you very much! I improved my answer, and asked a [new related question](https://stackoverflow.com/questions/51057861/how-to-wisely-interpret-this-compiler-warning), check it out if you like. – gsamaras Jun 27 '18 at 08:20
  • C11 J.2: "***[Undefined Behaviour](http://port70.net/~nsz/c/c11/n1570.html#J.2)** [if] Pointers that do not point into, or just beyond, the same array object are subtracted*" – alk Jul 01 '18 at 12:52

2 Answers2

6

The duplicate question mentions that:

pointer subtraction yields the number of array elements between two pointers of the same type

Read more about it in Pointer subtraction confusion.

However, your code is wrong and ill-formed, since it invokes Undefined Behavior. Please compile with warnings enabled, and you will get:

main.c: In function ‘main’:
main.c:12:7: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     p = arr[1];
       ^
main.c:13:7: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     q = arr[3];
       ^
main.c:15:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
     printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);
            ^
main.c:15:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘int *’ [-Wformat=]
main.c:15:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘int *’ [-Wformat=]

The errors will occur nevertheless. For the warnings, I just used the -Wall flag.


In order for your code to make sense, you could just declare p and q as simple ints and and not as pointers.

Or, you could do this:

p = &arr[1];
q = &arr[3];

printf("P-Q: %td, P: %p, Q: %p", (p - q), (void *)p, (void *)q);

and get something like this:

P-Q: -2, P: 0x7ffdd37594d4, Q: 0x7ffdd37594dc

Note that I used %td for printing the result of the subtraction of pointers.

gsamaras
  • 66,800
  • 33
  • 152
  • 256
6

Strictly speaking, what happens depends entirely on your compiler and platform... but let's assume we're using a typical compiler and ignoring the warnings.

Let's simplify your question further:

p = 2;
q = 4;

printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);

which produces the same wacky result:

P-Q: -1, P: 2, Q: 4

As @gsamaras pointed out, we're trying to subtract two pointers. Let's try and see how this might result in -1:

p - q = (2 - 4) / sizeof(int)
      = (-2)    / 4
      = -1

I suggest trying a couple of your own p and q values to see what happens.


Examples with different p and q:

p - q = ??
==========
0 - 0 =  0
0 - 1 = -1
0 - 2 = -1
0 - 3 = -1
0 - 4 = -1
1 - 0 =  0
1 - 1 =  0
1 - 2 = -1
1 - 3 = -1
1 - 4 = -1
2 - 0 =  0
2 - 1 =  0
2 - 2 =  0
2 - 3 = -1
2 - 4 = -1
3 - 0 =  0
3 - 1 =  0
3 - 2 =  0
3 - 3 =  0
3 - 4 = -1
4 - 0 =  1
4 - 1 =  0
4 - 2 =  0
4 - 3 =  0
4 - 4 =  0

Generated using gcc -fpermissive on:

#include <stdio.h>

int main() {
    printf("p - q = ??\n");
    printf("==========\n");

    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            int* p = i;
            int* q = j;

            printf("%d - %d = %2d\n", p, q, (p - q));
        }
    }

    return 0;
}
Mateen Ulhaq
  • 18,406
  • 13
  • 75
  • 112
  • 1
    Nice answer, I hope Jabberwocky is OK with me for removing the duplicate, since our answers really added up. – gsamaras Jun 27 '18 at 07:36
  • @mateen-ulhaq this answer really cleared the concept, none of the experts/pro really tried to understand that what I want to discuss here rather than finding flaws in code, I knew that that code is ill-formated and it was intentional to clear out basic concepts of how things are works at backstage – anonymous Jun 27 '18 at 07:44
  • 1
    We all try to help @Mr.Anonymous. I am glad Mateen provided what you were looking for. However, you could have commented on my answer, asking for specific guidance ( tip for the future =) ). However, I am still wondering how you were able to compile and run the code you posted. – gsamaras Jun 27 '18 at 07:47
  • @gsamaras I know that eveybody tries to help here, I just wanted to appreciate answer which helped me and never meant to devalue other's contribution, If you feel offended, I am sorry. that wasn't my intention. – anonymous Jun 27 '18 at 07:54
  • Of course I am not offended @Mr.Anonymous. I just want to advice, cheers! – gsamaras Jun 27 '18 at 07:55
  • @gsamaras yes it compiled successfully with warnings https://i.gyazo.com/a1bb8344aa18db1f77db3749b457773b.png – anonymous Jun 27 '18 at 08:00
  • Is there really rounding applied before subtracting the address? Why would the compiler do this? Is this limited to architectures where access to random unaligned addresses is not allowed? – Gerhardh Jun 27 '18 at 09:39
  • @Gerhardh I was thinking of an architecture restricted to 32-bit aligned access... However, I tried `4 - 2` on my system which gave `0`. This suggests that `2` is not being rounded down to `0`. I'll edit my answer. – Mateen Ulhaq Jun 27 '18 at 10:00
  • Even for an architecture with restricted access an invalid address would invoke UB and why should a compiler add extra code for UB? I would assume that the rounding is part of integer division. But then it should just chop off the fractional part. – Gerhardh Jun 27 '18 at 10:38
  • @MateenUlhaq output again causing confusion, can you explain when it gets rounded down and when rounded up? – anonymous Jun 27 '18 at 11:50
  • With `gcc`, integer *division* seems to always round down: `-1 / 4 = -1`. Same with `-2, -3, -4`. – Mateen Ulhaq Jun 27 '18 at 12:00
  • 2
    @MateenUlhaq it's not the division but a right shift by 2, which [always rounded down](https://stackoverflow.com/q/1857928/995714) `-1 >> 2 = -1` no one uses `div` to divide by a power of 2. And you still get UB with those `%d` – phuclv Jun 28 '18 at 05:00