Why does i[arr] work as well as arr[i] in C with larger data types?
Asked Answered
M

6

12

It's fairly common knowledge that if you access an element of an array as arr[i] in C that you can also access the element as i[arr], because these just boil down to *(arr + i) and addition is commutative. My question is why this works for data types larger than char, because sizeof(char) is 1, and to me this should advance the pointer by just one char.

Perhaps this example makes it clearer:

#include <string.h>
#include <stdio.h>

struct large { char data[1024]; };

int main( int argc, char * argv[] )
{
    struct large arr[4];
    memset( arr, 0, sizeof( arr ) );

    printf( "%lu\n", sizeof( arr ) ); // prints 4096

    strcpy( arr[0].data, "should not be accessed (and is not)" );
    strcpy( arr[1].data, "Hello world!" );

    printf( "%s, %s, %s\n", arr[1].data, 1[arr].data, (*(arr+1)).data );
    // prints Hello world!, Hello world!, Hello world!
    // I expected `hold not be accessed (and is not)' for #3 at least

    return 0;

}

So why does adding one to the array pointer advance it by sizeof( struct large )?

Million answered 24/8, 2011 at 19:58 Comment(1)
See also: #5610798Doubletime
N
11

In C, pointer arithmetic is defined so that writing

ptr + k

does not advance the pointer by k bytes, but by k objects. Thus if you have a pointer to an integer array and writing

*(myIntArrayPointer + 3)

You are dereferencing a pointer to the element at index 3 in the array, not the integer that starts three bytes past the start of the object.

Similarly, if you subtract two pointers, you get the logical number of elements in-between them, not the total number of bytes. Thus writing

(myIntArrayPointer + 3) - myIntArrayPointer

yields the value 3, even though there are 3 * sizeof(int) bytes in-between them.

Hope this helps!

Nematic answered 24/8, 2011 at 20:1 Comment(0)
J
7

That's pointer arithmetic. When you write

some_pointer + i 

it is compiled as

some_pointer + (i * sizeof(*my_pointer))

(i is an int for that matter :) )

Justin answered 24/8, 2011 at 20:1 Comment(0)
W
2

This sort of question can always be answered by reading the C spec. Try it!

§6.5.6 Additive Operators, paragraph 8:

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression.

Windle answered 24/8, 2011 at 20:3 Comment(1)
It's available on this page: open-std.org/JTC1/SC22/WG14/www/standards - you'll want the WG14 N1256 linkRegarding
D
1

The array/pointer is typed. The compiler knows how big the "thing" is, in this case your struct.

And C is just defined that way; advancing a pointer by "one" will go to the next thing in the array. The compiler knows how far to go for that. This applies to any of the related and equivalent syntaxes:

*(arr + i)
arr[i]
i[arr]

for any type that the compiler knows about.

This is called "pointer arithmetic", and it has at least one more fun property: if you have two pointers to items in an array, you can subtract them to get the count of items between them, in objects (i.e. not bytes).

Dagmar answered 24/8, 2011 at 19:59 Comment(0)
D
0

arr is declared as the array of 4 structures with each structure comprising of a char array of 1024 chars.

arr is resolved as the pointer to the first element to this array of structures. When you increment arr by 1, this new pointer, will skip one whole structure ( the type to which it points ) and then points to the next element in the array.

It all boils down to the compiler knowing the type to which a pointer points to, and then if you increment the array, it will point to the next element of the similar type in a contiguous memory location.

In this case if base address of the array is XYZ, then arr+ i = XYZ + i* sizeof(arr)/sizeof(struct large)

Dinger answered 24/8, 2011 at 20:6 Comment(0)
T
0

As others say, the expression i[arr] is equivalent to *(i + arr).

It's important that this isn't interpreted as *(i + sizeof(*i) * arr), but as *(arr + sizeof(*arr) * i).

What makes i different from arr?

They differ in type: i is an integer type while arr is a pointer type. Consequently, the behavior of addition between a pointer and an integer is uniquely determined regardless of their order.

In C, pointers are often treated as just integers representing addresses, but their behavior differs from that of integer types. For example, while adding two pointers (pointer + pointer) is prohibited, operations such as pointer + integer, integer + pointer, and of course integer + integer are allowed.

Link: cppreference, "Additive operators" section.

Thamos answered 10/5, 2024 at 7:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.