Является ли неопределенное поведение возвращать неинициализированную, в конечном итоге неиспользованную структуру? - PullRequest
0 голосов
/ 08 июня 2018

Является ли UB возвращением структуры без ее инициализации, если единственное последующее использование находится в операторе инициализации, как показано ниже:

typedef struct { int x; } s;

s callee(void) {
  s ret;
  return ret;
}

void caller() {
  s dummy = callee();
}

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

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


Существует много конструкций, которые все компиляторы обрабатывали бы последовательно, когда был написан стандарт, но которые стандарт явно не определяет.Стандарт отмечает, что общий способ обработки действий, при которых Стандарт не налагает никаких требований, «задокументированно характерен для окружающей среды», но в обосновании отмечает, что решение о том, когда делать это, является вопросом качества выполнения, а несоответствие.Обоснование также признает, что реализация может соответствовать, хотя и низкого качества, но быть бесполезной, но авторы не считают необходимым тратить усилия на запрещение таких реализаций.

Существует два разумных и полезных способакачественный компилятор для типичной платформы может обрабатывать код, подобный приведенному выше:

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

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

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

К сожалению, несмотря на то, что на любой распространенной платформе практически не будет стоить позволить функциям безопасно возвращать неопределенные значения в тех случаях, когда вызывающие их собираются игнорировать, и вв некоторых случаях это приведет к более эффективному коду, чем было бы возможно в других случаях (например, если сказать, что учитывая:

extern volatile int vv1, vv2, vv3;
int foo(int mode)
{
  int result;
  vv1 = 1;
  if (mode & 1)
    result = vv2;
  if (mode & 2)
    result = vv3;
  return result;
}

, оператор "foo (0);" будет хранить 1 в vv1 без других побочных эффектов)cts позволит избежать необходимости заставлять программистов заставлять компиляторы генерировать ненужную нагрузку) разработчикам компиляторов стало модным находить «умные» способы использования факта, что стандарт не требует таких гарантий.Например, приведенный выше код может быть «оптимизирован» для:

int foo(int mode)
{
  vv1 = 1;
  if (!(mode & 2))
    return vv2;
  if (mode & 1)
    +vv2; // Access and ignore value
  return vv3;
}

Независимо от того, будет ли практическая ценность таких «оптимизаций» когда-либо превышать возможность позволить программистам позволять компиляторам избегать ненужных нагрузок, программисты, которые могут 'не нужно быть уверенным, что их код будет выполняться только на качественных реализациях, для которых нужно учесть «умные».

0 голосов
/ 08 июня 2018

Сначала рассмотрим этот похожий код:

s ret;
s dummy = ret;

Структура не может иметь представление прерываний (C11 6.2.6.1/6).Но этот код вызывает неопределенное поведение из-за предложения Itanium (C11 6.3.2.1/2), в котором говорится, что использование значения неинициализированного автоматического объекта, для которого никогда не берется его адрес, вызывает UB.

Так что этот код будетчетко определено:

s ret;
&ret;
s dummy = ret;

Подробнее об этом предложении см .: Является ли ^ a или aa неопределенным поведением, если a не инициализировано? .


Для версии с возвращаемым значением функции: в стандарте не указано, считается ли возвращаемое значение автоматическим объектом для целей раздела Itanium.Я бы сказал, что это не так, поскольку возвращаемое значение не описывается стандартом как объект.Но было бы хорошо, если бы кто-то, знакомый с Itanium ABI, мог прокомментировать, вызывает ли передача неинициализированной структуры через возвращаемое значение исключение NaT.

Вместо этого моя позиция заключается в том, что версия вызова функции имеетта же семантика, что и у версии назначения, о которой говорилось выше, то есть отправленный код UB, но добавление &ret; делает его четко определенным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...