tags:

views:

307

answers:

12

When I compiled the following code, it shows that y and the beginning of the array are 60 units apart. But according to my calculation, it should have been 4 * 10 (for the array) + 4 (for k) + 4 (for y) = 48. Also array[12] = 17 was assigned to element 12, since there's no element 12, the implementation should have gone to y and overwrite y with 17. Yet the console printed y = 10 instead... im really confused... Please help!

 #include <stdio.h>

 int main(void) {
    int x = 42;
    int y = 10;
    int k = 10;
    int array[10];

    array[12] = 17;
    printf("array starts at %d\n", &array[0]);
    printf("x has the value %d\n", x);
    printf("x is stored in location %d\n", &x);

    printf("y has the value %d\n", y);
    printf("y is stored in location %d\n", &y);
 }
+2  A: 

&x is the address of the variable "x", and has no relationship to your array, except that it happens to be allocated on the stack nearby.

To get the address of the "xth" item in the array, you need to use &array[x]

array[12] is off the end of the array, so you are overwriting another variable on your stack. This could mean that another variable (e.g. y) is overwritten, or it could lead to a catastrophic crash of your program, depending on what is stored at that locaton. You should only access the members array[0] to array[9] in a 10-element array.

Jason Williams
+6  A: 

This is called undefined behavior (writing to array[12] in a 10 element array), so by definition you cannot know what it should do. There is no runtime checking in C, so you can write to wherever you wish (well, for the sake of this example) and you can't know what exactly will happen.

Ed Swangren
what about the addresses of the array? why are they 60 units apart instead of 48 according to my calculation?
metashockwave: Which "addresses of the array" is that? What are the things that are 60 units apart?
Thomas Padron-McCarthy
the location where y is stored and the beginning of the array
metashockwave: Ok, so y and the beginning of the array are 60 units apart. And? The compiler can put the variables wherever it wants. There is no guarantee that they are stored in memory just after each other, or in the order you have defined them. Just as an example, the compiler might generate some extra temporary variables that it needs for the program to perform calculations, and put those extra variables right between your normal variables!
Thomas Padron-McCarthy
They should be 40 bytes apart not 48!. Just like x and y are 4 bytes apart not 8!. Assume that y start at location '0' then you would expect x to start at location 4 not 8 and etc.
discovlad
They *should be* wherever the compiler puts them. This is called implementation defined behavior, i.e., you don't need to worry about it and you should never write code that depends on such behavior.
Ed Swangren
+2  A: 

C doesn't guarantee a program to be as you've written it. It only guarantees that it is like what you've written. The compiler can move and optimize variables of your code as long as it doesn't change the the functionality of your code.

  • It's possible that some variables of your code are stored in the read-only memory and not even on the stack.

  • C doesn't guarantee that the stack uses memory in reverse order.

Georg
+1  A: 

There's no guarantee that local variables are on the stack; the compiler could have decided to put some variables into registers. Assuming alignment and register issues are resolved, x would be at array[12] anyhow, since array[9] is the last valid place in array[].

Doing this sort of thing is unportable at best, and always a bad idea.

A: 

This is the problem with direct memory access, and why so many languages don't allow it now.

You can allocate an array of size 10, write to array location of 100 and it will work, but, you are now overwriting memory that may actually be used by another program, or by your program, and so you may have corrupted the actually application.

The only memory you can safely use is that which you have allocated to your program. You have no idea where the x and y ints will be in your application, as they could be anywhere in memory, just that there are 4 bytes set aside for your variable.

James Black
+2  A: 

There is no guarantee that your variables are laid out in memory in any specific relationship to each other. Trying to change array[12] is undefined behavior. It might change y, it might crash your program, it might do something else.

Now, having said that, it is possible to look at addresses to try to find out how your particular compiler laid out the variables in your particular program, this particular time it compiled it. To try this on my computer, I had to change your address printing, since my pointers are 64 bits, and your program tried to print them using ints, which are 32 bits. But after changing that, it turns out that y was placed 56 bytes after the start of the array, not 60 as your compiler did.

Then I compiled with optimization turned on, and now y was 40 bytes after the start of the array (just after the end of the array) and x 44 bytes after (that is, just after y). array[12] is after that too (remember, the last element in the array is numbered 9!), but by changing your array[12] to array[11], I got the program to print x as 17.

But, again, remember that all this is undefined behavior, and you should not write programs that depend on variables being placed by the compiler in any specific order.

Thomas Padron-McCarthy
+2  A: 

C does not guarantee that the variables that you define at the top of a function are laid out in memory exactly one after the other, so you cannot assume anything about the location of variable k with regard to the location of the array in memory.

That it's 60 instead of 48 is probably because the compiler aligns data in memory in a certain way (i.e. it deliberately skips some bytes) to make it more efficient for the CPU to access the data (the CPU can get data from memory quicker if it's in multiples of 8 or 16 bytes, for example - how this works depends on the details of your CPU). But really, k and the array could just as well have been a million bytes apart.

Note that your array contains 10 values. Trying to access elements outside the range 0 to 9 is not right, as you're doing (array[12]). C doesn't check array bounds, but if you access an array with an invalid index, strange things can happen - your program could crash or produce unpredictable results.

Jesper
+1  A: 

Two things

  1. The reason why a[12] works is because that array is last thing on the stack. Another words there is nothing "above" so you can keep writing it. Try switching array declaration with lets say 'x' and you most likely get a core dump

  2. You printing memory address as "signed ints" so they are probably
    coming out as negative numbers. I would change '%d' to %u to see positive number. You will see that the difference between them is 40 and not 60. Not sure how you are getting 60, may be you subtracted them incorrectly.

discovlad
A: 

Question's been already answered, but I'd like to point out, that if you need that kind of behaviour, you could use a struct instead.

struct {
    int array[10];
    int k;
    int x;
    int y;
} s;

s.k = s.y = 10;
s.x = 42;
s.array[12] = 17;
printf("array starts at %d\n", s.array);
printf("x has the value %d\n", s.x);
printf("x is stored in location %d\n", &s.x);

printf("y has the value %d\n", s.y);
printf("y is stored in location %d\n", &s.y);
PiotrLegnica
I do no think that it guarantees the expected behaviour: the struct is not always packed. On some machines./compilers, it may be impossible to pack it without breaking alignment rules.
bortzmeyer
+5  A: 

When I compile this code (gcc on OSX), it tells me that y and your array are 8 bytes apart. That's exactly what I would expect... the locals are being laid out like this:

int x = 42;
int y = 10;
int k = 10;
int array[10];

0                   1                   2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3...etc.
\__,__/ \__,__/ \__,__/ \__,__/ \__,__/ \__,__/
   x       y       k      [0]     [1]     [2]
                          array

The numbers are offsets from the bottom of the stack frame. So x fills the bottom 4 bytes, 'y' is stacked on top of it, and so on. The array is 40 bytes long, at the very top.

&y is the address of the start of y, which is +4 bytes from the bottom of the stack frame. &array[0] is the address of the start of the array, which is +12 bytes from the bottom of the stack frame.

By the way, you should probably format your addresses as 'unsigned' - %u. Without that, large addresses might come out as negative numbers.

array[12] is beyond the end of all this, so there's no way you should expect it to affect y. If you want to play with undefined behaviour, then array[-2] might be equivalent to y. To get it to work like that, I had to compile with optimisation off - your mileage may vary :)

alex tingle
Thank you Alex!I was taught in a different way:local variables follow overhead meaning thatxxxxyyyykkkkarray[10]array[10]array[10]array[10]array[9]array[9]array[9]array[9]array[8]......so on
The layout of those variables is not defined by the language, so any implementation is *possible*. In particular, your program doesn't actually use variable k, and I found it quite tricky to persuade the compiler to include it on the stack.
alex tingle
A: 

As other people have mentioned, this is undefined behavior under the standard, and could do pretty much anything. In addition If I change the printf formats for the addresses to the more-appropriate "%p", the output I get on Mac OS X looks like this:

array starts at 0xbffff7d4
x has the value 42
x is stored in location 0xbffff7cc
y has the value 10
y is stored in location 0xbffff7d0

So, in my case, the array is stored at a higher address than x or y. In order to see the write past the end of the array, the declarations would apparently need to be switched so that the x and y variables are allocated before the array.

When I do that, I get this:

array starts at 0xbffff7cc
x has the value 42
x is stored in location 0xbffff7f4
y has the value 10
y is stored in location 0xbffff7f8

So, now they're in the right order, at least. There's no extra padding between the variables, though, so I need to adjust the overwrite to array[10], to get this:

array starts at 0xbffff7cc
x has the value 17
x is stored in location 0xbffff7f4
y has the value 10
y is stored in location 0xbffff7f8

Success, of a sort - I've managed to overwrite the value of "x" through an access to "array". As it turns out, changing almost anything in the compiler settings will change the addresses of the various variables. This is what makes writing stack-smashing security exploits as difficult as it is...

Mark Bessey
A: 
int array[10];

    array[12] = 17;

very funny...

This is a question ?

Arabcoder