Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C, is it guaranteed that the array start address is smaller than the other elements' addresses?

In other word when doing

index = &array[x] - &array[0];

Is it always guaranteed (per C standard) that &array[0] <= &array[x], or is it dependent on the compiler? What are the C standard chapters relevant for this topic ?

like image 920
gramm Avatar asked Jul 19 '17 09:07

gramm


People also ask

Are arrays fixed-size in C?

Arrays a kind of data structure that can store a fixed-size sequential collection of elements of the same type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type.

How can I get array elements without knowing the size in C?

int reqArraySize; printf("Enter the array size: "); scanf("%d", &reqArraySize); After this you may proceed with this interger input array size : for(i=0;i<reqArraySize;i++) scanf("%d",&arr[i]);

Is the address of the first element in the array?

When you have created an array the first element in the array is referred to as a[0], the first memory address of the array.

What will happen if we access elements beyond the size of an array?

Accessing array elements greater than its size If you try to access the array position (index) greater than its size, the program gets compiled successfully but, at the time of execution it generates an ArrayIndexOutOfBoundsException exception.


2 Answers

The address ordering is guaranteed. The behaviour of relational operators is defined in C11 6.5.8p5:

[...] pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. [...]

Thus &array[x] >= &array[0] is true always if x is the index of an element, or one greater than the maximum index. (And if x is not the index of an element, or one past the end of the actual array, then behaviour is undefined.)

But surprisingly the difference &array[x] - &array[0] is defined only when

  • x is an actual index of an element or one greater than the maximum index in the array and
  • x is not greater than PTRDIFF_MAX

as there is a peculiar corner case: C11 6.5.6p9 says that

9 When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i-j provided the value fits in an object of type ptrdiff_t.[...]

If the signed ptrdiff_t is of same width as the unsigned size_t, it is possible to have an array for which there exists an index x greater than PTRDIFF_MAX; then &array[x] >= &array[0] still, but &array[x] - &array[0] has completely undefined behaviour.


Here is a demonstration. My computer is x86-64 that runs 64-bit Ubuntu Linux, but it is also capable of running 32-bit programs. In 32-bit X86 Linux + GCC, ptrdiff_t is a 32-bit signed integer, and size_t is 32-bit unsigned integer. A program run in 64-bit Linux in 32-bit mode can easily allocate over 2G of memory with malloc, as the entire 4G address space is reserved for user mode.

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stddef.h>

int main(void) {
    size_t size = (size_t)PTRDIFF_MAX + 2;
    size_t x = (size_t)PTRDIFF_MAX + 1;
    char *array = malloc(size);
    if (! array) {
        perror("malloc");
        exit(1);
    }
    array[0] = 42;
    array[x] = 84;
    printf("&array[0]: %p\n", (void *)&array[0]);
    printf("&array[x]: %p\n", (void *)&array[x]);
    printf("&array[x] >= &array[0]: %d\n", &array[x] >= &array[0]);
    printf("&array[x] - &array[1]: %td\n", &array[x] - &array[1]);
    printf("&array[x] - &array[0]: %td\n", &array[x] - &array[0]);
    printf("(&array[x] - &array[0]) < 0: %d\n", (&array[x] - &array[0]) < 0);
}

Then compiled for 32-bit mode and run:

% gcc huge.c -m32 -Wall && ./a.out 
&array[0]: 0x77567008
&array[x]: 0xf7567008
&array[x] >= &array[0]: 1
&array[x] - &array[1]: 2147483647
&array[x] - &array[0]: -2147483648
(&array[x] - &array[0]) < 0: 1

The memory was allocated successfully, the starting address is at 0x77558008, &array[x] is at 0xf7504008, &array[x] is greater than &array[0]. The difference &array[x] - &array[1] produced a positive result, whereas &array[x] - &array[0], with its undefined behaviour, now produced a negative result!

like image 160

First of all, FWIW, quoting C11, chapter §6.5.6/P9, (emphsis mine)

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. [...]

So, you don't need to be bothered about the individual pointer value (positioning) itself. It's the difference that matters (i.e, something like |a-b|)


That said, if it has to come to the "comparison", ( usage of relational operators, <, >, <=, >=), the standard says,

When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. [....] If the objects pointed to are members of the same aggregate object, [...] and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. [....]

So, for a statement like &array[x] <= &array[0], it will evaluate to 0 (FALSY), when x > 0.

Thanks to the other answer by Joachim

like image 41
Sourav Ghosh Avatar answered Oct 06 '22 11:10

Sourav Ghosh