tags:

views:

590

answers:

4

Possible Duplicate:
In C arrays why is this true? a[5] == 5[a]

Is the possibility of both array[index] and index[array] a compiler feature or a language feature. How is the second one possible?

+5  A: 

From the earliest days of C, the expression a[i] was simply the address of a[0] added to i (scaled up by the size of a[0]) and then de-referenced. In fact, all these were equivalent:

a[i]
i[a]
*(a+i)

====

The only thing I'd be concerned about is the actual de-referencing. Whilst they all produce the same address, de-referencing may be a concern if the types of a and i are different.

For example:

    int i = 4;
    long a[9];
    long x = a[i]; //get the long at memory location X.
    long x = i[a]; //get the int at memory location X?

I haven't actually tested that behavior but it's something you may want to watch out for. If it does change what gets de-referenced, it's likely to cause all sorts of problems with arrays of objects as well.

====

Update:

You can probably safely ignore the bit above between the ===== lines. I've tested it under Cygwin with a short and a long and it seems okay, so I guess my fears were unfounded, at least for the basic cases. I still have no idea what happens with more complicated ones because it's not something I'm ever likely to want to do.

paxdiablo
How is it different today ??
Ben
I don't think it is different. I meant it dates from the earliest days, not that it was only like that in the early days.
paxdiablo
This is wrong. Either way, the compiler knows adding an int to a long* (in either order) results in a long*, which is then dereferenced to long. And this applies for any pointer type, including user-defined.
Matthew Flaschen
Wrong how. @Matthew? I've already stated that my suspicions weren't correct and that standard types worked okay. As to more complicated types, I haven't made a comment simply because it's not anything I've tried (or am likely to try).
paxdiablo
Why did you post this when you voted to close? The dup had plenty of good answers, but you should have at least made this CW.
Zifre
I posted this answer long before someone else found the dupe. Then I agreed with that decision to close after I checked out the dupe. It's not rocket science. And, really, it's not up to you to decide how people answer questions or whether they make them CW. It's up to the answerers themselves. If this was not a suitable question for SO, I would have gone CW (or more likely just left a comment) but this *is* a suitable question, just duplicated.
paxdiablo
+21  A: 

The compiler will turn

 index[array]

into

*(index + array)

With the normal syntax it would turn

array[index]

into

*(array + index)

and thus you see that both expressions evaluate to the same value. This holds for both C and C++.

Martin Geisler
This 'transformation' illustration only holds when one of the variables is a pointer or an array and the other is an integer type, i.e. the usual C case. In C++, a[b] loses its symmetry as operator[] can be an overloaded member function and the compiler is not allowed to use *(a+b) instead. *(a+b) might have a different type and a different value or it might not be a valid expression at all.
Charles Bailey
+1. Cool. Never thought of it that way.
Magnus Skog
@Charles: good point!
Martin Geisler
+2  A: 

As Matthew Wilson discusses in Imperfect C++, this can be used to enforce type safety in C++, by preventing use of DIMENSION_OF()-like macros with instances of types that define the subscript operator, as in:

#define DIMENSION_OF_UNSAFE(x)  (sizeof(x) / sizeof((x)[0]))

#define DIMENSION_OF_SAFER(x)  (sizeof(x) / sizeof(0[(x)]))

int ints[4];

DIMENSION_OF_UNSAFE(ints); // 4
DIMENSION_OF_SAFER(ints); // 4

std::vector v(4);

DIMENSION_OF_UNSAFE(v); // gives impl-defined value; v likely wrong
DIMENSION_OF_SAFER(v); // does not compile

There's more to this, for dealing with pointers, but that requires some additional template smarts. Check out the implementation of STLSOFT_NUM_ELEMENTS() in the STLSoft libraries, and read about it all in chapter 14 of Imperfect C++.

edit: some of the commenters suggest that the implementation does not reject pointers. It does (as well as user-defined types), as illustrated by the following program. You can verify this by uncommented lines 16 and 18. (I just did this on Mac/GCC4, and it rejects both forms).

1
2 #include <stlsoft/stlsoft.h>
3
4 #include <vector>
5
6 #include <stdio.h>
7
8 int main()
9 {
10         int     ar[1];
11         int*    p = ar;
12         std::vector<int>        v(1);
13
14         printf("ar: %lu\n", STLSOFT_NUM_ELEMENTS(ar));
15
16 //      printf("p: %lu\n", STLSOFT_NUM_ELEMENTS(p));
17
18 //      printf("v: %lu\n", STLSOFT_NUM_ELEMENTS(v));
19
20         return 0;
21 }
22
dcw
A better 'dimension' implementation can be achieved with templates:template <typename T, std::size_t N> std::size_t dimension( T[N] }. That will be safer than the sizeof(a)/sizeof(a[0]) with arrays (that have not decayed into pointers).
David Rodríguez - dribeas
dribeas: the problem with that is that it's no longer a compile-time expression. The technique described by Wilson illustrates a compile-time version. As I said, it's available in the STLSoft libraries.
dcw
There is also a way to get a compile time expression: template<typename T, std::size_t N> char ( That will evaluate to a reference to a char array having the same size. Use it like: int v[sizeof an_array(another_array)];
Johannes Schaub - litb
Note that both mine and dribea's macro are different from wilson's one, of course. Our one will reject a pointer. His one will accept it. Thus, these are different use cases.
Johannes Schaub - litb
tlib, dribeas: you're both wrong. See the edited response above.
dcw
@dcw, no i'm not wrong. the code compiles fine with comeau and gcc and there is no reason it should ever fail. Note that i'm not talking about STLsoft's version. I'm talking about the macro you have shown us.
Johannes Schaub - litb
A: 

In C and C++ (with array being a pointer or array) it is a language feature: pointer arithmetic. The operation a[b] where either a or b is a pointer is converted into pointer arithmetic: *(a + b). With addition being symetrical, reordering does not change meaning.

Now, there are differences for non-pointers. In fact given a type A with overloaded operator[], then a[4] is a valid method call (will call A::operator ) but the opposite will not even compile.

David Rodríguez - dribeas