8

In C it is possible to reverse the operands of array subscripting and still achieve the same result. For example a[b] == b[a]. This is because (according to C11 draft N1570, §6.5.2.1/2) a[b] is identical to (*((a)+(b))), and + is commutative. For more, see this question.

Is there ever a scenario (in C, where there's no operator overloading) where swapping the operands of the [] operator doesn't result in the same value at runtime? So, does there exist an a and b for which a[b] != b[a]? (assuming the program compiles and a[b] == a[b])

Community
  • 1
  • 1
user12341234
  • 6,573
  • 6
  • 23
  • 48

5 Answers5

5

You seem mostly to have answered your own question with your reiteration of why a[b] means the same thing as b[a]. That explanation is correct and founded directly on the standard. The main caveat is that a and b must have types and values such that evaluating a[b] has defined behavior at all. As long as that is the case, the two expressions must evaluate to the same thing. Otherwise, C has nothing to say about whether the two expressions evaluate to the same thing.

On a slightly different tack, if a and b are permitted to represent expressions more complicated than variable names and constants, then it is possible to choose them such that evaluating the overall conditional expression (a)[b] == (b)[a] has undefined behavior, even though evaluating the left-hand and right-hand subexpressions individually has defined behavior. For example,

char array[2] = "a";
int index = 0;
// undefined:
_Bool condition = array[index++] == (index++)[array];

Overall, however, there is no case in which C defines the expression (a)[b] != (b)[a] to be true, regardless of the type, form, or values of a and b.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I suppose a generalization of your example where `a` and `b` are function calls that can make arbitrary side-effects could test in which order they're called and behave suitably to satisfy my constraints without having any undefined behavior. This is relatively unsatisfying though. – user12341234 Feb 18 '17 at 20:25
  • 1
    @user12341234, since evaluation of the left-hand and right-hand sub-expressions are not sequenced relative to each other, I'm inclined to think that hiding order-dependency inside functions does not render the behavior defined. That the compiler may not be able to *recognize* the problem does not rescue the situation. But this comment is somewhat off the cuff, so if you're inclined to research it against the text of the standard then feel free. – John Bollinger Feb 18 '17 at 20:34
3

Well, if you count this as a valid counterexample:

int *i;
*i[i]; // does not compile
i[*i]; // does compile

The reason is that the subscript operator has higher precedence than the indirection operator, so *i[i] is equivalent to *(i[i]), which is invalid because the subscript operator cannot take two pointer types.


Note that those examples yield undefined behavior when actually executed, of course. It's only supposed to answer the OP's question.

cadaniluk
  • 15,027
  • 2
  • 39
  • 67
  • 2
    You could easily make the example defined by changing the first line to, say, `int *i = &(int){0};`. – Kerrek SB Feb 18 '17 at 19:48
  • 2
    Upvote regardless, but in `*i[i]`, `*i` is not an operand of the subscript, whereas in `i[*i]` it is. – user12341234 Feb 18 '17 at 19:50
  • @KerrekSB I don't want to introduce any further syntax other than precisely what the OP relates to. (Call that exaggerated, I think it's appropriate.) Furthermore, this counterexample can be demonstrated by compiling the program, it needn't be run, so making it well-defined seems superfluous to me. – cadaniluk Feb 18 '17 at 19:52
  • @user12341234 Your actual question is if there is a case where `a[b] != b[a]`, that is, the *values* of those expressions differ. Strictly speaking, I'm not answering this. Instead, I'm showing you an expression that compiles and one that doesn't. Since you acknowledge my answer as valid, your question leaves room for interpretation: are you asking if `a[b]` and `b[a]` differ at *runtime* (as in your question) or at *compile time*, i.e., at source-level (as in my answer)? I only answer the 2nd, so either you tell me my answer is off-track or you accept that I'm answering slightly differently. – cadaniluk Feb 18 '17 at 20:04
  • 1
    Both :). I'm asking this question largely from an academic perspective, so your current answer is valuable to me in that I learned something. As the question was originally formulated in my head, I was interested in see an example where runtime values differed. – user12341234 Feb 18 '17 at 20:08
  • This is using different precedence of `*` and [] operators. `*i[i]` has different meaning to `i[*i]`, since it is equivalent to `*(i[i])`, not to `(*i)[i]`. `(*i)[i]` and `i[(*i)]` both compile, and produce the same result. – Peter Feb 18 '17 at 23:52
0

Consider the following program:

#include <stdio.h>

int x[2][2] = { { 1, 2 }, { 3, 4 } };
int *y = x[0];
int (*z)[2] = x;

#define a (*y)
#define b *z

int main(void) {
    printf("a[b] = %d\n", a[b]);
    printf("b[a] = %d\n", b[a]);
    return 0;
}

The output will be:

a[b] = 2
b[a] = 3

Like this answer, it takes advantage of the fact that [] has higher precedence than unary *. So:

  • a[b] &rightarrow; (*y)[*z] &rightarrow; (1)[x[0]]x[0][1] &equals; 2
  • b[a] &rightarrow; *z[(*y)] &rightarrow; *x[(1)]x[1][0] &equals; 3
Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • As my comment above, in `b[a]`, `b` is not an operand of the subscript, only `z` is – user12341234 Feb 18 '17 at 21:44
  • @user12341234 True. But my answer compiles, and the tokens appear as your question posits. – jxh Feb 18 '17 at 22:43
  • In an expression `b[a]`, both `b` and `a` are operands of the operator `[]`. It's sort of akin to `a+b` having two operands `a` and `b`, even though one is to the left of the `+` and one to the right. – Peter Feb 18 '17 at 23:54
0

Yes - for operator precedence concerns in a macro body

While casually skimming the WebKit source, I found an example where the author intentionally reversed the operands to an array access.

#define ARRAY_SIZE(x) \
  ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

Note that in two places, the author uses 0[x] instead of the more traditional x[0]. The reason for this, I assume, is that this usage happens inside a macro, rather than in raw code. Since x is in fact a parameter to the macro, its value will be textually substituted into the macro body. A consequence of this is that x does not have to be a single identifier, but perhaps in infix expression. In this case, there is potential for operator precedence issues to creep in. Take for example this usage:

int a[] = {0, 1};
ARRAY_SIZE(a); // Expands as 0[a]

int b[] = {0, 1};
ARRAY_SIZE(b+1); // Expands as 0[b+1]

If the operands were reversed, the two calls to ARRAY_SIZE would expand to a[0] and b+1[0] respectively. The former works as intended, but the latter is a likely segmentation fault since it's equivalent to b+(1[0]).

Effectively, by reversing the operands to the array access, the author has obviated the need to proactively parenthesize their macro parameters. This might not be an incredibly powerful usage, but it is a small benefit to reversing operands, at the (likely much greater) expensive of readability.

user12341234
  • 6,573
  • 6
  • 23
  • 48
-1

in int *i; *i[i]; // like number[pointer] i[*i]; // does like pointer[number]

and pointer[number] is the way that you go to a member in an array.

also the i[*i] will not compile unless you will change int *i; to int *i; *i=value;

when value is a number.

Roy Avidan
  • 769
  • 8
  • 25
  • 4
    This doesn't seem to address the OP's question and I frankly don't understand what you are trying to tell us. How does answer if there's a case where `a[b] != b[a]`? – cadaniluk Feb 18 '17 at 21:11