Ex 15: reading past array length

I have been working through the book Learn C The hard way, and I’m now
at Exercise 15: Pointers.

So I was playing around with the code trying to change it to see what
will happen, what breakage will occur etc etc.
So I came up with the following;

#include <assert.h>
#include <stdio.h>

int main()
{
    int ages[] = { 23, 43, 12 };
    // setup the pointers to the start of the arrays
    int* cur_age = ages;

    printf("cur_age_pointer: %p \n", cur_age);

    printf("cur_age_val: %d \n", *cur_age);
    assert(*cur_age == 23);
    assert(*cur_age + 1 == 24);

    printf("cur_age_val_offset_1: %d \n", *(cur_age + 1));
    assert(*(cur_age + 1) == 43);
    assert(*(cur_age + 1) == ages[1]);

    printf("oops: %d \n", *(cur_age + 19000)); // What is happening here??

    return 0;
}

I can’t understand why the line printf("oops: %d \n", *(cur_age + 19000)); succeeds.
Is that a problem? Are we reading some other processes’ memory?

I thought that if I changed the definition of the ages array to;
int ages[3] = { 23, 43, 12 }; // ie add explicit size
that it would help. But it doesn’t. Even if I run the code with

  • -Wall -Wextra -Werror
  • valgrind
    Those tools do not pick up any error.

I’m I mistaken in believing that there ought to be a bug in *(cur_age + 19000) ?

If I’m reading that right you took the cur_age pointer and moved it 19000 ints forward. It should be the same as doing this:

cur_age[19000];

Now, C doesn’t care what you do with pointers, so “works” just means you didn’t hit a bad part of memory, but I’m wondering why valgrind didn’t flag this. Maybe I’m reading that wrong since my C is rusty, but try running it under gdb (lldb on OSX) and see if that explodes. Also, what’s the output of this?

Then, you’re reading from that spot, which might be just fine, but try writing to it and see what happens. After all that, if you can’t make it crash, then I guess that’s C for ya. There’s way to make it crash but, I’d say just move on from here and try with another program.

running with gdb --batch --ex run --ex bt --ex q --args [program] does not explode.

I’m also able to write to that location.

printf("oops: %d \n", *(cur_age + 19000)); // What is happening here??
printf("oops: %d \n", cur_age[19000]);
cur_age[19000] = 88;
printf("oops: %d \n", cur_age[19000]);

The complete output using gdb is;

cur_age_pointer: 0x7ffffffde158 
cur_age_val: 23 
cur_age_val_offset_1: 43 
oops: 1869770855 
oops: 1869770855 
oops: 88 
[Inferior 1 (process 884320) exited normally]
No stack.

Thanks for your help.

I wont dwell on this much, I’ll swiftly move along and file this under C’s weirdness.

Ah, see how oops is

oops: 1869770855

That means it’s working since it’s printing out garbage. Most likely there’s some junk in the RAM at that point and you’re just getting lucky it doesn’t cause a crash. Don’t worry, you’ll cause plenty of crashes, but this shows you how you could get a “weird number” on accident.

Keep this in mind in the future. If you’re ever running code and it’s spitting out bizarre answers, you’ve probably accidentally done this.

A free service run by Zed A. Shaw for learncodethehardway.org.