Как подразумевается «тип со знаком или без знака» в этом неопределенном определении поведения C90? - PullRequest
0 голосов
/ 28 февраля 2019

В стандарте ANSI C90 в разделе 6.3 говорится об выражениях:

Объект должен иметь свое сохраненное значение, доступ к которому имеет только lvalue, который имеет один из следующих типов: [...] тип, который является типом со знаком или без знака, соответствующим квалифицированной версии объявленного типа объекта

И в Приложении G. есть этот случай неопределенного поведения2:

Поведение в следующих обстоятельствах не определено: [...] У объекта есть сохраненное значение, к которому обращается lvalue, который не имеет один из следующих типов: объявленный тип объектаобъект, квалифицированная версия объявленного типа объекта, тип со знаком или без знака, соответствующий объявленному типу объекта, тип со знаком или без знака, соответствующий квалифицированной версии объявленного типа объектаагрегированный или объединенный тип, который (рекурсивно) включает в себя один из вышеупомянутых типов среди своих членов, или символtype (6.3).

Я нахожу формулировку выделенных частей неоднозначной и пытаюсь ее истолковать.

  1. Означает ли это "тип со знаком, соответствующийисходному типу, если он был подписан, или типу без знака, соответствующему исходному типу, если он был без знака ";или "тип (не имеет значения, подписанный или без знака) соответствует исходному типу"?То есть:

    signed int a = -10;
    unsigned int b = *((unsigned int *) a);
    

    ... undefined?

  2. Если значение со знаком / без знака не имеет значения, учитывая, что в стандарте проводится различие между тремятипы char, signed char и unsigned char, будет ли определен доступ к char через signed char * или unsigned char *?

Ответы [ 3 ]

0 голосов
/ 28 февраля 2019

Обратите внимание, что Приложение G является информативным, а соответствующая часть для цитирования нормативной. C90 6.3.

Это относится к предшественнику "правила строгого наложения имен", позднее введенного в C99.В C90 было неоднозначно, что делать с объектами, не имеющими типа, такими как данные, на которые указывает возврат из malloc.

  1. Это означает, что если типобъект имеет значение signed int или unsigned int, доступ к lvalue можно получить с помощью signed int* или unsigned int*.Эти два типа указателей разрешены для псевдонима.Так, например, если у вас есть такая функция:

    void func (signed int* a, unsigned int* b)
    

    , то компилятор не может предположить, что a и b указывают на разные объекты.

    (Обратите внимание, что дико экзотические системытеоретически может иметь биты заполнения и представления прерываний для подписанных типов, поэтому доступ к unsigned int через signed int* может быть UB по другим причинам, в теории.)

  2. Типы символовдействительно являются частным случаем по сравнению с другими целочисленными типами.Но это не имеет значения, поскольку в правиле есть особый случай: «или тип символа».char, unsigned char и signed char - все типы символов.Это означает, что любой доступ указателя к lvalue с использованием любого из этих 3 типов четко определен.

    Тип lvalue даже не должен быть типом символа!Например, вы можете получить доступ к lvalue с int по signed char*, и он четко определен, но не наоборот.

0 голосов
/ 28 февраля 2019

Когда был написан C89, неподписанные типы были достаточно новым дополнением к языку, в котором большой код использовал int в тех местах, где unsigned - как только он существовал - имел бы больше смысла.Авторы стандарта хотели обеспечить, чтобы функции, которые использовали более новый тип unsigned, могли обмениваться данными с теми, которые были написаны для использования int, поскольку unsigned еще не существовало.

Стандарт немного неоднозначен относительно того, имеет ли тип, такой как unsigned*, «соответствующий тип со знаком» int*, или unsigned** имеет «соответствующий тип без знака» int** и т. Д. Учитывая цель разрешения взаимодействиямежду кодом, который предшествует неподписанным типам, и кодом, который их использует, создание функции, написанной для работы с последовательностями int*, непригодной для клиентов с последовательностью unsigned*, противоречило бы этой цели, а также уставу Комитета.Поддержка заявленной цели не потребует универсального использования int** для доступа к объектам типа unsigned*, но потребует, чтобы компиляторы с такими конструкциями, как:

unsigned *foo[10];
actOnIntPtrs((int**)foo, 10);

, распознавали, что вызываемая функция может влиять на объектытипа unsigned* хранится в foo.

0 голосов
/ 28 февраля 2019

Это говорит о том, что не неопределенное поведение, чтобы привести значение к другой подписи.Если объект объявлен signed int, вы можете получить к нему доступ, используя unsigned int lvalue, и наоборот.

Случай, когда подпись одинакова, уже рассматривается, когда он говорит "объявленный тип объекта".объект ", хотя в этом случае можно также сказать, что.

В случае char оба signed char и unsigned char являются" типом со знаком или без знака, соответствующим "этому типу.

Все вместе это просто говорит о том, что подпись lvalue не влияет на то, является ли доступ четко определенным.

...