views:

249

answers:

5

Possible Duplicate:
Take the address of a one-past-the-end array element via subscript: legal by the C++ Standard or not?

int array[10];

int* a = array + 10;   // well-defined
int* b = &array[10];   // not sure...

Is the last line valid or not?

A: 

No. It's undefined. array[10] amounts to *(array + 10). In other words, you've dereferenced an invalid pointer.

Noah Roberts
But he didn't dereference it...
calmh
Tyler McHenry
Loadmaster
@Loadmaster Quite formally - yes it does dereference the element in C++ and C89, and thus causes undefined behavior in C89. In C++ it causes undefined behavior *if* there is no element just after that array (to be sure, you would have to put that subarray into a multidimensional array - then there is guaranteed to be an element just after another array without padding). In C++ if there is such an element it's fine likewise *because* such a pointer in and of itself is *not* invalid - it's valid to point a pointer past the end of an array. Just dereferencing it is constrained as said above.
Johannes Schaub - litb
"dereference" means to obtain an lvalue from a pointer. It does not mean to read a value. The Standard says for example at `5.3.1/1`: "[Note: a pointer to an incomplete type (other than cv void ) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to an rvalue, see 4.1. ]" for another example see `3.8/5` "Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below."
Johannes Schaub - litb
+12  A: 

Yes, you can take the address one beyond the end of an array, but you can't dereference it. For your array of 10 items, array+10 would work. It's been argued a few times (by the committee, among others) whether &array[10] really causes undefined behavior or not (and if it does, whether it really should). The bottom line with it is that at least according to the current standards (both C and C++) it officially causes undefined behavior, but if there's a single compiler for which it actually doesn't work, nobody in any of the arguments has been able to find or cite it.

Edit: For once my memory was half correct -- this was (part of) an official Defect Report to the committee, and at least some committee members (e.g., Tom Plum) thought the wording had been changed so it would not cause undefined behavior. OTOH, the DR dates from 2000, and the status is still "Drafting", so it's open to question whether it's really fixed, or ever likely to be (I haven't looked through N3090/3092 to figure out).

In C99, however, it's clearly not undefined behavior.

Jerry Coffin
Lee-Man
The language from that DR is not in N3092. Though I can't find them right now, there are several _closed_ DRs that rely on the "empty lvalue" language in the proposed resolution to that defect...
James McNellis
+1  A: 

array[10] is equivalent to *(array + 10) (and also equivalent to 10[array]), so &array[10] acts like removing the * from *(array+10). Any decent compiler should emit the same code (and the same warning).

Cogwheel - Matthew Orlando
+4  A: 

As other answers have indicated, the expression array[10] is equivalent to *(array + 10), which would appear to result in an undefined dereferencing of the element just past the end of the array. however, the expression &array[10] is equivalent to &*(array + 10), and the C99 standard makes clear (6.5.3.2 Address and indirection operators):

The unary & operator returns the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator.

So, there's nothing undefined about &array[10] - there is no dereference operation that takes place.

Michael Burr
C99 is not C++.
Noah Roberts
A: 

This is actually a fairly simple thing to answer.

All you are doing is pointer math, there is nothing invalid about it. If you try to USE the resulting pointers in some way then depends on whether or not your process has access to the memory pointed to by that pointer, and if so what are the permissions on it?

foo.cc:

#include <iostream>

using namespace std;
int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        cerr << "Pointers values are: " << a << " " << b << " " << c << endl;
        return 0;
}

g++ -Werror -Wall foo.cc -o foo

should yield NO warnings because the int *b = &array[10] is valid

Actually so is the subsequent line added by me to just point something out about pointer math.

not only will foo compile, it will run without a hitch:

 ./foo
  Pointers are: 0x7fff1d356e68 0x7fff1d356e68 0x7fff1d359d20

I'll post Visual C++ output as soon as my VM boots :-)

Basically array syntax foo[idx] is short cut and clean representation of pointer math.

In case there is some confusion regarding c99, the standard DOES NOT affect this math in anyway and well defined.

The same thing in C99:

#include <stdio.h>

int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        fprintf(stderr, "Pointers are: %p, %p, %p\n" , a , b , c );
        return 0;
}

gcc -std=c99 -Wall -Werror -o foo foo.c

./foo

Pointers are: 0x7fff2c7d22c8, 0x7fff2c7d22c8, 0x7fff2c7d5180
Elf King