1

If I dereference an array and add 5 which exceeds the elements available within the array what is the value that is printed? I am returning 32767 within my IDE which doesn't make much sense.

#include <stdio.h>

int main(void)
{

    int i;
    int meatBalls[5] = {1, 2, 3, 4, 5};

    printf("%12s %18s %6s\n", "Element", "Address", "Value");

    for(i=0; i < 5; i++) {
        printf("meatBalls[%d] \t %p \t %d \n", i, &meatBalls[i], meatBalls[i]);
    }

    printf("\nmeatBalls \t\t %p \n", meatBalls); /*already a pointer*/

    /*dereference meatBalls and it will update to the value e.g. 1*/

    printf("\n*meatBalls \t\t %d \n", *meatBalls); /*dereference with asterisk*/

    printf("\n*(meatBalls+2) \t\t %d \n", *(meatBalls+2)); /*dereference but also go through the array from 0 + 2*/

    printf("\n*(meatBalls+4) \t\t %d \n", *(meatBalls+4));

    printf("\n*(meatBalls+5) \t\t %d \n", *(meatBalls+5)); /*what is the number that this returns?*/

    return 0;
}

This is the current output and has made me question the "32767":

     Element            Address  Value
meatBalls[0]     0x7fff5b714640      1 
meatBalls[1]     0x7fff5b714644      2 
meatBalls[2]     0x7fff5b714648      3 
meatBalls[3]     0x7fff5b71464c      4 
meatBalls[4]     0x7fff5b714650      5 

meatBalls        0x7fff5b714640 

*meatBalls       1 

*(meatBalls+2)       3 

*(meatBalls+4)       5 

*(meatBalls+5)       32767 
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Wunderbread
  • 898
  • 2
  • 14
  • 34
  • 2
    It is printing whatever happens to be at that point in memory at that time. If you're doing something strange, why are you surprised to get a strange result? – AntonH May 31 '17 at 17:01
  • @GradyPlayer While most of us will _get_ the message, some of us may not (believe me, been there, seen that), better refrain from making those comments. Hope you got the part. :) – Sourav Ghosh May 31 '17 at 17:15
  • 1
    Well...That is undefined behavior for sure. Possible duplicate of https://stackoverflow.com/questions/10473573/why-is-out-of-bounds-pointer-arithmetic-undefined-behaviour – Pushan Gupta May 31 '17 at 17:22

2 Answers2

6

TL;DR: Don't know, can't say.

To elaborate, in your code

  printf("\n*(meatBalls+5) \t\t %d \n", *(meatBalls+5));

causes undefined behavior as you try to access out of bound memory.

Please note the emphasis on access. The pointer arithmetic in this case is well-defined (one past the last element), attempt to access in what causes the UB.

Quoting C11, chapter §6.5.6 (emphasis mine)

[...] if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

That said, %p expects its argument to be a void* and since printf() is a variadic function, no default argument promotion takes place. So, you need to cast all the arguments to %p to (void *)

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • 4
    In this context I always feel a compulsion to point out that, while it is legal to form a pointer to one past the end of an array object, it is _not_ legal to form a pointer to one before the beginning of an array object. – ad absurdum May 31 '17 at 17:11
  • @DavidBowling oh.. didn't know that. I don't dispute it, but I have to ask, is there some reasoning behind that rule? It would see to me that you could assemble any old pointer as long as you do nothing with it? – ThingyWotsit May 31 '17 at 17:45
  • @ThingyWotsit-- ["If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.](http://port70.net/~nsz/c/c11/n1570.html#6.5.6p8) The "otherwise... undefined" part is crucial. This can come up when iterating an array in reverse. Going off the beginning may _seem_ to "work", but is UB. – ad absurdum May 31 '17 at 17:50
2

Accessing elements beyond an array's bounds is undefined behaviour; so you can get any value, but your program could also crash. Here's how the C standard defines undefined behaviour:

3.4.3 undefined behavior

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements.

NOTE: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

And that's where the standard defines that your access is undefined behaviour:

J.2 Undefined behavior

1 The behavior is undefined in the following circumstances:

...

  • An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a1[7] given the declaration int a[4][5])
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58