Эффективные типы выделенных объектов и структур - PullRequest
0 голосов
/ 30 января 2019

Из спецификации c99 я не совсем понимаю, что происходит с эффективным типом выделенного ниже объекта.

typedef struct {
    int x;
    char y;
} MyStruct ;

MyStruct *make_struct (void) {
    MyStruct *p = malloc(sizeof(MyStruct));
    p->x = 1;
    p->y = 2;

    /* what is the effective type of the allocated object at this point? */

    return p;
}

Когда вы присваиваете значение выделенному объекту, эффективный тип выделенного объектастановится типом lvalue, используемым для магазина, но что здесь используется lvalue?

Насколько я понимаю из 6.5.2.3p4 ...

Постфиксное выражениеза которым следует оператор ->, а идентификатор обозначает член структуры или объекта объединения.Это значение именованного члена объекта, на которое указывает первое выражение, и является lvalue.Если первое выражение является указателем на квалифицированный тип, результат имеет так называемую версию типа назначенного члена.

... тип "x-> y"expression - это тип y (но только если x указывает на квалифицированный тип).
Итак, у меня есть выделенный объект без эффективного типа и два «внутренних объекта» с типами int и char?

Как странно ..

Редактировать: Предположим, эффективный тип * p заканчивается как int.Это неопределенное поведение тогда?Кто-то получит доступ к объекту через lvalue с типом MyStruct.Доступ к члену также подразумевает доступ к агрегатному типу?Это продолжает давать ..

Ответы [ 3 ]

0 голосов
/ 30 января 2019

Цитаты из C99 6.5 / 6

эффективный тип объекта для доступа к его сохраненному значению является объявленным типом объекта, еслиany.

  • malloc(sizeof(MyStruct)); На этом этапе возвращенные данные не имеют действующего типа.
  • MyStruct *p = malloc(sizeof(MyStruct)); По-прежнему нет действующего типа, p простоуказывает на данные, не сохраняя ничего.
  • p->x = 1; Действующее правило типа:

    Если значение сохраняется в объекте, у которого нет объявленного типа, через lvalue, имеющийтип, который не является символьным типом, тогда тип lvalue становится эффективным типом объекта для этого доступа и для последующих доступов, которые не изменяют сохраненное значение.

    Поскольку мы имеем int x; значение выражения p->x = 1; равно int и оно становится эффективным типом того, что хранится в p->x.

  • В случае p->y значение l используется длядоступ к объектам является символьным типом, поэтому указанное выше правило не применяется.И при этом это не скопировано как массив символов.Мы заканчиваем в последнем предложении правила:

    Для всех других обращений к объекту, у которого нет объявленного типа, эффективный тип объекта - это просто тип lvalue, используемого для доступа.

    Значение эффективного типа p->y становится char, поскольку lvalue выражения p->y = 2; равно char.

6.5.2.3 / 4 здесь не имеет значения, за исключением «... и является lvalue».

*p не имеет эффективного типа как такового, потому что мы никогда не обращались к области памяти через полный тип структуры.Однако такое выражение, как MyStruct m = *make_struct();, по-прежнему четко определено, поскольку правило строгого псевдонима разрешает доступ к объектам структуры, учитывая, что структура содержит члены, совместимые с действующими типами.В этом случае структура содержит int и char членов, которые полностью совместимы с действующими типами, с которыми данные, на которые ссылаются p->x и p->y, заканчиваются.

0 голосов
/ 19 марта 2019

Термин «объект», используемый повсюду в черновике C11 (N1570), за исключением 6.5p6 («Правило действующего типа»), относится к области хранения, которая связана с каким-то конкретным типом.Если int *p является допустимым ненулевым указателем, он будет указывать на объект типа int «на» или «только что прошедший».Похоже, что 6.5p6 использует термин «объект» для обозначения некоторой области хранения, которая может быть или не быть на самом деле объектом, но остальная часть Стандарта не использует термин «объект» таким образом.Помимо прочего, в спецификации для malloc не говорится, что он возвращает указатель на объект или что он создает объект, а скорее, что он возвращает указатель на область памяти, достаточно большую, чтобыДержите объект заданного размера.

Поскольку 6.5p6 использует термин «объект» способом, который противоречит его использованию везде, его значение будет зависеть от того, как вы решите определить этот термин.В отсутствие сноски 87 («Выделенные объекты не имеют объявленного типа.») Можно решить эту проблему, просто заметив, что эффективный тип каждого объекта - это просто его тип.Это на самом деле работало бы просто отлично, если бы кто-то распознавал области хранения как удерживающие суперпозицию всех объектов, которые могли бы в них поместиться, но интерпретировал сноску 88 из 6.5p7 («Цель этого списка - указать те обстоятельства, при которых объект может или можетне быть псевдонимом. ") говоря о том, что единственными" объектами ", к которым применяется правило, являются те, которые используются в том же контексте, что и lvalue, без того, чтобы их не использовали заново и визуально при выводе этого lvalue.

Какоднако в сноске 87 четко указывается, что пункт 6.5p6 должен использовать значение «объект», отличное от всего остального в Стандарте, не уточняя, что это за значение.Я не думаю, что возможно сформулировать определение, которое разумно обрабатывает все угловые случаи, и кажется сомнительным, что авторы Стандарта имели согласованное значение для того, что вещи были или не были «объектами» для целей 6.5p6 или 6.5p7,Следовательно, значение 6.5p6 и 6.5p7, а также то, что будет на их основе допустимо, будет в значительной степени зависеть от того, как читатель выберет смысл слова «объект».

0 голосов
/ 30 января 2019

Выделенный блок не имеет действующего типа, потому что (1) у него нет объявленного типа, и (2) он не был назначен.Части блока, которые соответствуют элементам x и y, имеют действующие типы, но не весь блок.

Отсутствие действующего типа не означает неопределенное поведение: каждый член MyStructвернувшемуся из make_struct был дан правильный действительный тип индивидуально, поэтому код, обращающийся к членам возвращенного struct, остается действительным.

Ваш фрагмент кода может быть изменен для использования составного литерала для инициализации всего MyStruct, а не для инициализации его компонентов.Это сделало бы эффективный тип выделенного блока MyStruct:

MyStruct *make_struct () {
    MyStruct *p = malloc(sizeof(MyStruct));
    *p = (MyStruct){.x = 1, .y = 2};
    return p;
}

Примечание: Этот ответ был значительно отредактирован после обновления вопроса.

...