confusing on pointer and array

2020-07-10 07:23发布

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?

标签: c
4条回答
倾城 Initia
2楼-- · 2020-07-10 07:39

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  | ?  | ?  | ?  | ?  | ?  |
查看更多
3楼-- · 2020-07-10 07:48

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;-).

查看更多
smile是对你的礼貌
4楼-- · 2020-07-10 07:50

"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).

查看更多
神经病院院长
5楼-- · 2020-07-10 07:54

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.

查看更多
登录 后发表回答