Мне показалось, что я действительно понял это, и перечитывание стандарта (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
имеет тип "pointer to int", а значением является начальный адрес my_test
. *type2_p->ptr
поэтому оценивается как целочисленный объект, чье хранилище находится по тому же адресу, что и у my_test
.
Далее:
6.2.2.1 L-значения, массивы и функциональные обозначения
«За исключением случаев, когда это операнд
размер оператора или одинарный &
оператор, ..., lvalue, который имеет
тип array of type
преобразуется в
выражение с типом pointer to
type
, которое указывает на начальный
элемент массива объекта и не является
lvalue. "
my_test.array
имеет тип «массив int» и, как описано выше, преобразуется в «указатель на int» с адресом первого элемента в качестве значения. *my_test.array
поэтому оценивается как целочисленный объект, чье хранилище находится по тому же адресу, что и первый элемент в массиве.
И наконец
6.5.2.1 Структура и спецификаторы объединения
указатель на объект структуры,
соответствующим образом преобразован, указывает на его
начальный член ... и наоборот.
Там может быть безымянный отступ в пределах
структура объекта, но не на его
начало, по мере необходимости для достижения
соответствующее выравнивание.
Поскольку первый член type1_t
является массивом, начальный адрес
этот и весь объект type1_t
такой же, как описано выше.
Поэтому я понимаю, что *type2_p->ptr
оценивает
целое число, чье хранилище находится по тому же адресу, что и первый
элемент в массиве и, следовательно, идентичен *my_test.array
.
Но это не может иметь место, потому что программа последовательно падает
на солярисе, cygwin и linux с версиями gcc 2.95.3, 3.4.4
и 4.3.2, поэтому ни о какой экологической проблеме не может быть и речи.
Где мои рассуждения неверны / что я не понимаю?
Как мне объявить type2_t, чтобы ptr указывал на первый член массива?