0

This example compiles without warnings/errors (gcc 4.8.2 -Wall):

#include <stdio.h>
int main() 
{ 
    char c;
    int i;    
    printf("%p %02x\n",&i,c[&i]);
    printf("%p %02x\n",&c,c[&c]);

    // important to note that this line DOESN'T compile:
    //printf("%p %02x\n",&i,c[i]);
    //  which makes sense as c is NOT an array, it is a char.

    return 1;
}

Why does the syntax c[&i] compile ? Is it intentional or an accident? Is the syntax c[&i] semantically valid ? (Does it have any useful meaning?)

Example of output for me: (pointers change each time)

0xbfb2577c b7718cea
0xbfb2577b 08

This question originated from a strange bit of code 'ch2[&i]' in question here: C duplicate character,character by character

NOTE#0 (updated, upon reflection) on duplicates/similar questions: This question is not a duplicate of the linked questions on c array references. It is related so it is useful to refer to them. The related questions discuss the valid a[b] and b[a] case where one of a or b is a pointer and the other is an int. This question deals with the more weird and maybe should be invalid case where one of a or b is a char. With C arrays, why is it the case that a[5] == 5[a]? 14 answers With arrays, why is it the case that a[5] == 5[a]? String as an array index 3 answers String as an array index

NOTE #1: This happens with compiler as the type of variable c is char and that can be used as index into array when combined with pointer.

NOTE #2: for some reason, type of c[<ptr>] evaluates to type of <ptr>.

E.g.: the resulting types of c[&pi] and c[&pc] cause warnings in the following code:

int *pi; char *pc; pi=&i; pc=&c;
printf("%p %02x\n",&pi,c[&pi]);
printf("%p %02x\n",&pc,c[&pc]);

Warnings on type 'int *' or 'char *' instead of 'unsigned int':

c/so_cweirdchar2.c: In function ‘main’:
c/so_cweirdchar2.c:13:5: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int *’ [-Wformat=]
     printf("pi %p %02x\n",&pi,c[&pi]);
     ^
c/so_cweirdchar2.c:14:5: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘char *’ [-Wformat=]
     printf("pc %p %02x\n",&pc,c[&pc]);
     ^
Community
  • 1
  • 1
gaoithe
  • 4,218
  • 3
  • 30
  • 38

2 Answers2

4

In C, the [] postfix pointer displacing operator is commutative. If a[i] is a valid expression, then so is i[a] and means the same thing. Though we have to watch operator associativity and precedence to make sure we do this reversal right. Your c[&i] means the same thing as (&i)[c]: the pointer &i displaced by the integer c.

The basis for the commutativity of [] is the equivalence between indexing and the combination of pointer displacement and dereferencing, where the + operator is commutative:

E1[E2]  <-->  *(E1 + E2)
  ^               ^
  |               |
  v               v
E2[E1]  <-->  *(E2 + E1)

Here we understand E1 to be fully parenthesized expressions, so we are unconcerned about precedence. In effect we are manipulating abstract syntax trees in which E1 and E2 are nodes.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • Cool. Okay. Good explaination. I partially understand now. It works if value inside [] is integer. The weird thing is expressions like c[i] will not compile if c is a plain variable, e.g. char c; But using a pointer as reference seems to trick the compiler into allowing you to subscript plain variables. – gaoithe Oct 18 '16 at 22:12
  • Another thing of note is that I'm seeing the type of a[&b] to come out as type b. This is when 'a' is char (not char*). Not quite what one might expect. – gaoithe Oct 18 '16 at 22:26
  • 1
    `c[i]` requires one of the operands to be a pointer and one an integer. This is because it is basically `*(c + i)`. The `c + i` part, if we look at it that way, would, in theory, take any arithmetic types, like `int` and `float`; but the result will not dereference with the unary `*`; that requires a pointer. The only additive expressions that produce pointers are *pointer* + *some integer type* or vice versa. – Kaz Oct 18 '16 at 22:37
  • 1
    Interesting to note that the compiler flags (as a warning I presume) in `c[i]` that `c` is not an array, but does not flag in `c[&i]` (or `(&i+c)`) that this is not an array. In the first, the programmer is informed he indexes something that is not an array (`c`) but in the second he is not informed he indexes something else that is neither an array (`i`), – Paul Ogilvie Oct 18 '16 at 22:40
  • I see the compiler throws c[i] as error. Rightly so :0) who would want to index into a single char c. No error or warning on c[&i]. – gaoithe Oct 18 '16 at 22:49
  • @PaulOgilvie It's a useful diagnostic because taking advantage of this commutativity is basically nothing but bad style. It probably works that way because it was easy to implement in some early C compiler as an abstract syntax tree transformation (popularly called "desugaring" nowadays). Making that transformation only work when the pointer is on the left would have required more lines of code (adding a test against it). – Kaz Oct 18 '16 at 22:51
0

Compiles with no error. Perfectly valid C:

int main (int argc, char** argv)
{
    int n=5;
    char *a="abcdefghijk";

    printf("%c\n", a[n]);
    printf("%c\n", n[a]);
    return 0;
}
stark
  • 12,615
  • 3
  • 33
  • 50
  • But 'char c; c[n];' does not compile. "error: subscripted value is neither array nor pointer nor vector" – gaoithe Oct 18 '16 at 22:00
  • 1
    Correct. You need one integer and one pointer, which OP has. Note that if I change `int n=5;` to `char n=5;` my example still compiles and runs fine. – stark Oct 18 '16 at 22:14
  • "one integer and one pointer" is useful to keep in mind a[b]. thanks. Add note that c casts happily char or any pointer to/from int. I could see your example and understand it by itself but didn't get fully what you were trying to explain with it. – gaoithe Oct 18 '16 at 22:22