5

Can someone explain, why

int main(void)
{
    char *a = NULL;
    int *b = NULL;
    double *c = NULL;

    printf("\n%d %d %d\n%d %d %d\n\n",
    (int) (a+1), (int)(b+1), (int)(c+1),
    (int) (a+5), (int)(b+7), (int)(c+17));
    return 0;
}

outputs

1, 4, 8
5, 28, 136

I think it has something to do with the byte-sizes of those variables. But I don't understand the second line of output.

Amit Chauhan
  • 6,151
  • 2
  • 24
  • 37
Saphire
  • 1,812
  • 1
  • 18
  • 34

5 Answers5

15

If p is a pointer to type T, and it holds memory address X, then p + N is the memory address X + N * sizeof(T).

(int) (a +  1) == 0 +  1 * sizeof(char)   ==  1 * 1 ==   1
(int) (b +  1) == 0 +  1 * sizeof(int)    ==  1 * 4 ==   4
(int) (c +  1) == 0 +  1 * sizeof(double) ==  1 * 8 ==   8

(int) (a +  5) == 0 +  5 * sizeof(char)   ==  5 * 1 ==   5
(int) (b +  7) == 0 +  7 * sizeof(int)    ==  7 * 4 ==  28
(int) (c + 17) == 0 + 17 * sizeof(double) == 17 * 8 == 136

Minor note: As Barmar points out in his answer, arithmetic on NULL pointers is technically undefined behavior. So a standard-compliant compiler could have done something different with that arithmetic; the code might have printed 1, 3.1415, or a picture of dogs playing poker. But the behavior you observed is the same as the defined behavior you would've observed if you'd started with valid pointers, so you probably don't need to worry about this.

TypeIA
  • 16,916
  • 1
  • 38
  • 52
4

In C, pointer arithmetic depends on the size of the underlying type.

When you do p + n on some pointer p you don't really add n to it. Instead you add n * (sizeof(*p)). That, is you advance the pointer by n items.

Nik Bougalis
  • 10,495
  • 1
  • 21
  • 37
3
a+5 = 0 + sizeof(char)*5 = 5
b+7 = 0 + sizeof(int)*7 = 28
c+7 = 0 + sizeof(double)*17 = 136
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

Arithmetic on null pointers results in undefined behavior, so any result is possible.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Should I start worrying that using offsetof macro starts to format my hard drive? (Well, you didn't say anything is possible under UB... but still...) – Aki Suihkonen Feb 28 '14 at 19:12
  • @AkiSuihkonen: No, as this macro is evaluated a compile-time. The question isn't about using `NULL`, but about reading pointers carring `NULL`. – alk Feb 28 '14 at 19:14
  • 2
    Technically true, but the OP would've observed the same effect even if he started with valid non-NULL pointers. So this answer doesn't really answer the spirit of the question, which is the fact that pointer arithmetic is based on the size of the pointed-to type. – TypeIA Feb 28 '14 at 19:15
  • 1
    Under UB, I'd be still expecting that (int)a == (int)b == (int)c, and (c+5)-c == 5 * sizeof(double), regardless of encoding of NULL. – Aki Suihkonen Feb 28 '14 at 19:21
2

Arithmetic operations on NULL-pointers are undefined behaviour.

From the C-standard:

6.3.2.3 Pointers

[...] 3 If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

[...]

6.5.6 Additive operators

[...] 2 For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to an object type and the other shall have integer type.

[...]

Apx J.2 Undefined behavior

[...]

Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that does not point into, or just beyond, the same array object (6.5.6).

alk
  • 69,737
  • 10
  • 105
  • 255