Я думал, что это действительно понял, и повторное чтение стандарта (ISO 9899: 1990) просто подтверждает мое явно неправильное понимание, поэтому теперь я спрашиваю здесь.
Сбой следующей программы:
#include <stdio.h>
#include <stddef.h>
typedef struct {
int array[3];
} type1_t;
typedef struct {
int *ptr;
} type2_t;
type1_t my_test = { {1, 2, 3} };
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
type1_t *type1_p = &my_test;
type2_t *type2_p = (type2_t *) &my_test;
printf("offsetof(type1_t, array) = %lu\n", offsetof(type1_t, array)); // 0
printf("my_test.array[0] = %d\n", my_test.array[0]);
printf("type1_p->array[0] = %d\n", type1_p->array[0]);
printf("type2_p->ptr[0] = %d\n", type2_p->ptr[0]); // this line crashes
return 0;
}
Сравнение выражений my_test.array[0]
и type2_p->ptr[0]
в соответствии с моей интерпретацией стандарта:
6.3.2.1 Подписывание массива
"Определение индекса оператор [] состоит в том, что E1 [E2] идентичны (* ((E1) + (E2))).
Применяя это, получаем:
my_test.array[0]
(*((E1)+(E2)))
(*((my_test.array)+(0)))
(*(my_test.array+0))
(*(my_test.array))
(*my_test.array)
*my_test.array
type2_p->ptr[0]
*((E1)+(E2)))
(*((type2_p->ptr)+(0)))
(*(type2_p->ptr+0))
(*(type2_p->ptr))
(*type2_p->ptr)
*type2_p->ptr
type2_p->ptr
имеет тип "указатель на int", а значение является начальным адресом my_test
. Таким образом, *type2_p->ptr
оценивает целочисленный объект, чье хранилище имеет тот же адрес, что и my_test
.
Далее:
6.2.2.1 Lvalues, массивы и обозначения функций
"За исключением случаев, когда это операнд оператор sizeof или унарный & оператор,..., l значение, которое имеет тип
array of type
преобразуется в выражение с типомpointer to type
, которое указывает на начальную элемент объекта массива и не является lvalue."
my_test.array
имеет тип "array of int" и как описано выше, преобразован в "указатель на int" с адресом первого элемента как значения. Таким образом, *my_test.array
оценивает целочисленный объект, чье хранилище находится на том же адресе, что и первый элемент в массиве.
И наконец
6.5.2.1 Спецификации структуры и объединения
Указатель на объект структуры, соответствующим образом преобразованный, указывает на его начальный элемент... и наоборот. Внутри объект структуры, но не начиная, по мере необходимости, соответствующее выравнивание.
Так как первым элементом type1_t
является массив, начальный адрес
что и весь объект type1_t
тот же, что описан выше.
Поэтому я понял, что *type2_p->ptr
оценивает
целое число, чье хранилище имеет тот же адрес, что и первый
элемент в массиве и, таким образом, идентичен *my_test.array
.
Но это не может быть так, потому что программа постоянно срабатывает на Solaris, cygwin и linux с версиями gcc 2.95.3, 3.4.4 и 4.3.2, поэтому любая экологическая проблема не может быть и речи.
Где мои рассуждения неправильные/чего я не понимаю? Как объявить type2_t, чтобы ptr указывал на первый член массива?