Означает ли отбрасывание значения чтение? - PullRequest
7 голосов
/ 15 октября 2019

Рассмотрим следующий пример:

{
  int x;
  (void)x; // silence the "unused" warning
  ...
}

Приводит ли это к неопределенному поведению из-за того, что x читается неинициализированным? Если да, то означает ли это, что в следующем коде компилятором должна быть выдана инструкция чтения памяти (для чтения pointee)?

volatile char* p=getP();
(void)*p;

Меня интересуют правила C и C ++, касающиесяэто в случае, если они отличаются.

Ответы [ 2 ]

1 голос
/ 15 октября 2019

Соответствующая часть из стандарта C:

6.3.2.2 void

(несуществующее) значение выражения void (выражение, имеющее тип void) никоим образом не должно использоваться, и неявные или явные преобразования (кроме void) не должны применяться к такому выражению. Если выражение любого другого типа оценивается как пустое выражение, его значение или обозначение отбрасывается. (Пустое выражение оценивается на предмет его побочных эффектов.)

Где побочные эффекты определены в 5.1.2.3/2:

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

Чтение энергонезависимой переменной не является побочным эффектом.

То есть, если доступ к x является побочным эффектом, то код должен быть оценен (выполнен). Это только тот случай, когда x равен volatile. Так что (void)x; не вызовет неопределенного поведения.

В противном случае было бы неопределенным поведение использовать локальную переменную x, потому что ее адрес никогда не берется нигде внутри области действия.

В случае *p у вас есть ясное lvalueдоступ к переменной с изменяемыми значениями через оператор *, поэтому компилятор должен прочитать переменную. Независимо от актерского состава до (void).

Пример ниже также будет оценен, но вызывает неопределенное поведение (если не взят адрес самого указателя p):

char* volatile p;
(void)p;
1 голос
/ 15 октября 2019

В C ++ доступ связан с преобразованием lvalue в rvalue. Это преобразование - то, что берет "идентичность" и производит ее ценность. Что касается выражений отброшенных значений, то стандарт C ++ гласит следующее:

[expr] (выделено мной)

12 Inв некоторых контекстах выражение появляется только для его побочных эффектов. Такое выражение называется выражением отброшенного значения. Стандартные преобразования массива в указатель и функции в указатель не применяются. Преобразование lvalue в rvalue применяется тогда и только тогда, когда выражение является glvalue типа volatile-квалифицированного и является одним из следующих :

  • (выражение),где выражение является одним из этих выражений,
  • id-выражение,
  • подписка,
  • доступ к члену класса,
  • косвенное обращение,
  • операция указатель на член,
  • условное выражение, где оба и третий операнд являются одним из этих выражений, или
  • выражение запятой, где правый операнд является одним из этих выражений.

[Примечание: использование перегруженного оператора вызывает вызов функции;вышесказанное охватывает только операторы со встроенным значением. - примечание конца] Если выражение является предварительным значением после этого необязательного преобразования, применяется временное преобразование материализации. [Примечание: если выражение является lvalue типа class, оно должно иметь конструктор volatile для инициализации временного объекта, являющегося результирующим объектом преобразования lvalue-to-rvalue. - end note] Выражение glvalue вычисляется, и его значение отбрасывается.

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

В изменчивом случае, однако, у вас есть косвенность внутри выражения приведения, и это подвергается преобразованию lvalue в rvalue. Таким образом, volatile читается как , и вы получаете неопределенное поведение при чтении неинициализированного объекта.

Чтобы избежать неопределенного поведения вокруг volatile glvalues, можно аннотировать соответствующие переменные как [[maybe_unused]]. Это санкционированный способ, и я бы предпочел его вместо броска в нелетучем случае.

...