tags:

views:

129

answers:

4

We have

 int a[5]={10, 20, 30, 40, 50};

I would like to know how does the following two code segment do?

 int *ptr = (int *)(&a+1);
 int *t = (int *)(&a -1);

If we have

 printf("%d  %d  %d \n", *(a+1), *(ptr-1), *(t+1));

What should be the result?

A: 

Let's look at it piece by piece.

&a means the address of a. So, it gets the address of the address of the integer 10.
&a+1 is the next pointer over from that. So it's the thing stored after the variable a in memory. Bad idea.
&a-1 is the thing stored before a in memory. Again, bad idea.

*(a+1) is the thing at the location pointed to by a, plus one integer. That would be a[1], or 20.
*(ptr-1) is a, because ptr is &a+1, so ptr-1 is &a. It's the pointer value of a. Printing it as %d is a mistake. If you were to say **(ptr-1), you'd get a more-meaningful 10 from the printf.
*(t+1) is also a, as per the above but with the pluses and minuses switched.

Borealid
Alex Martelli
-1 For some inaccurate information
NullUserException
Borealid
@Borealid, you're simply, desperately wrong all the way -- have you **tried** some `printf("%p\n", (void*)(eachofthese))` for each of the pointer values `eachofthese` that you're mentioning, for goodness' sake?!
Alex Martelli
caf
@caf: Given `sizeof(int)==sizeof(int*)`, they do. See my previous comment.
Borealid
For example, ` printf("%p %p %p\n", (void*)
Alex Martelli
@caf, right -- but @Borealid just won't listen. Let's hope he does the `printf` I've suggested (and abundant variations thereof to his heart's contents;-) and thereafter repents (or will he go flat-earther, claim his compiler is buggy, etc?-)...
Alex Martelli
@Borealid: Those are the wrong types - the relevant sizes here are `sizeof(int)` and `sizeof(int [5])`, which clearly cannot be equal.
caf
+4  A: 

All the problems come from the use of &a, which is a pointer to "an array of five integers", so that pointer arithmetic (when you think in terms of addresses) gets "scaled" by sizeof(a) (which might e.g. be 20 if int are 4 bytes and the compiler needs no padding for alignment purposes -- reasonable hypotheses, though far from certain of course.

So, after

int *ptr = (int *)(&a+1);
int *t = (int *)(&a -1);

ptr is a pointer to int at the memory address "sizeof(a) more than the address a", and t similarly for "sizeof(a) less than the address of a". Therefore...:

 printf("%d  %d  %d \n", *(a+1), *(ptr-1), *(t+1));

What should be the result?

Quite possibly a segmentation violation, otherwise 20 followed by two completely arbitrary integer values. Since ptr and t are pointers to int, the address arithmetic scaling for their -1 and +1 does not compensate that done on &a (the scaling in terms of memory addresses is by sizeof(int), not sizeof(a)!), so ptr-1 and t+1 are pointing to (alleged;-) ints that are respectively "a few ints after the end of a" and "a few ints before the start of a".

There's no way to know whether at those arbitrary addresses there is any memory which the process is allowed to address (whence the possibility for segmentation violatons), and, if any accessible memory is there, what its contents "seen as an int" might possibly be.

Edit: @caf points out that ptr - 1 is not invalid -- it correctly points to the last element of a; so the output (unless there's a segmentation fault, which @NullUserException thinks is very unlikely but on this point we disagree;-) would start with 20 50 before the third, "arbitrary" junk. Point is, per the C standard, it is valid to compute (though not to use) the pointer "just one past the end" of an array, and the sizeof an array must be exactly that array's length time the sizeof its elements (padding is allowed for an element's type, if needed, and if so it shows in the element's own sizeof, but not for the array as a whole). Subtle, but important;-).

Alex Martelli
You're not quite right here. `a`, the pointer to an array of integers, is not necessarily stored next to `*a`, the integers themselves. The issue is not the scaling, but rather the level of referencing at which the arithmetic is done. Take a closer look...
Borealid
@user297850, Alex: The segfault/random memory dereference was exactly what I got, which you can see in the output mentioned in my answer.
Merlyn Morgan-Graham
Borealid
Alex Martelli
caf
By the way, `*(ptr - 1)` is OK - it points at the last `int` in `a`. `*(t + 1)` is garbage, though.
caf
Borealid
@Merlyn It's not a segfault if you've got an answer.
NullUserException
caf
@Alex: `a[1] = 20`, so shouldn't it be "20 followed by two completely arbitrary integer values" ?
Lazer
@Alex Just a couple of minor corrections: (1) `*(a+1)` is 20, not 10 (2) `*(ptr-1)` is guaranteed to point at `a[4]`, (3) `*(t+1)` points at `a[-4]`, which is garbage, but very unlikely to segfault the program.
NullUserException
@Borealid, "Did anyone bother to run test code other than me? -- of course, see my `printf` example in a comment to your A. Did _you_ run that `printf` yet? Just put it in a `main` right after the declaration of `a` as per the Q, then `return 0`, the rest doesn't matter. What numbers do you see from your compiler? How do you interpret those numbers given your utter misunderstanding of C's behavior wrt arrays and pointers?-)
Alex Martelli
@Lazer, yep, tx and +1 -- fixing now.
Alex Martelli
@cal, right, tx, +1 -- though we disagree about the "unlikely to segfault", that's not part of the C standard anyway;-). I've edited the A to explain why I was wrong (computing, though not using, the address "one past the end" of an array _is_ guaranteed to be valid in C, _and_ `sizeof` of an array is not subject to compiler whim;-).
Alex Martelli
@Alex I say it's "unlikely to segfault" because they are equivalent to `a[5]` and `a[-5]`, or in case of `*(ptr-1)` and `*(t+1)`, `a[4]` and `a[-4]`
NullUserException
@NullUserException: I get what you are saying. I saw that someone posted an answer to the effect of segfault, and replied in kind. Either way it is still a broken program :)
Merlyn Morgan-Graham
A: 

"What should be the result"?

Next time you want to know what a tiny code snippet like this should do, check this out: http://ideone.com/4fCud

The result I got out of that was:

20 50 134520820

Edit:

When you run a program, see output like this, and find yourself asking, "where did that value come from?" you may have run into undefined behavior.

In this case the third value doesn't point to where you might think it would point. It is reading uninitialized memory (most likely), memory owned by code that is in your process space, but outside your program (a library you loaded, or the C runtime), or memory that simply has nothing to do with this program (less likely, because of protected memory).

Merlyn Morgan-Graham
Uhh... you do realize the third number is garbage based on whatever happened to be in memory at the time, right?
darron
@darron: Yep, I know that. The point of the post was to demonstrate that there were compilers online for simple questions like this, so the author need not ask it to begin with. The better question for him to ask would have been "why isn't it outputting the values I expect?"
Merlyn Morgan-Graham
@Merlyn: The problem is just that your answer doesn't mention the fact that the last number is garbage... which is important here. If someone wrote "20 50 134520820" as the answer to this on a test it'd be wrong.
darron
@darron: I don't think that the test analogy applies here. Discussions are provided in a QA format, but that simply lowers the barrier to entry. I don't think Stack Overflow's purpose is *just* to provide answers, but to increase understanding. I provided the answer the way I did because I didn't want to duplicate other people's efforts and information, and felt there was some value to the information I added.
Merlyn Morgan-Graham
@darron: However, at your persistence, I can add a note explaining the program's output :)
Merlyn Morgan-Graham
+5  A: 

Since the type of a is array-of-5-ints, that means that the type of &a is pointer-to-array-of-5-ints.

When you add or subtract 1 from a pointer, you ask it to point to the next or previous object of that type in memory. So &a+1 is creating a pointer to the array-of-5-int immediately after a in memory (which doesn't exist), and &a-1 is creating a pointer to the array-of-5-int immediately before a in memory (which also doesn't exist). In memory, it looks like this (where each cell represents one int):

Address:    &a-1                      &a                      &a+1
Contents:  | ?  | ?  | ?  | ?  | ?  | 10 | 20 | 30 | 40 | 50 | ?  | ?  | ?  | ?  | ?  |

When a is used in the expression *(a+1), it is converted to a pointer to its first element - so a pointer-to-int pointing at the 10 value. Adding one to it then makes a pointer pointing at the next int - a+1 points at the 20 value. *(a+1) then fetches that value, so the first number printed is 20.

As ptr is also a pointer-to-int, that means that ptr - 1 creates a pointer to the int immediately before ptr - in this case, it'll be pointing at the 50. So the second number printed is 50.

Similarly, t + 1 creates a pointer to the int immediately after t - in this case, it's the second ? in the above diagram. This is an uninitialised value - it could print anything at all, or even crash the program.

Address:    &a-1                      &a                       &a+1
            t    t+1                  a   a+1            ptr-1 ptr
Contents:  | ?  | ?  | ?  | ?  | ?  | 10 | 20 | 30 | 40 | 50  | ?  | ?  | ?  | ?  | ?  |
caf
NullUserException
@Null, yes, but, more simply, `int* ptr = a + 1;` would be just fine -- `a`, being an array of `int`s, "decays" to "pointer to `int`" when used in an expression. No casts, nor complications, needed;-).
Alex Martelli
@NullUserException: A very nice observation, and one that shows clearly how the effect of the `+1` changes depending on the type of the pointer it's applied to.
caf