Неопределенное поведение sizeof (* ptr) при указании на недопустимую память?

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

Но каково правило при использовании в выражении, переданном sizeof ?

Например:

 int *ptr = 0; int size = sizeof(*ptr); 

Это также не определено?

В большинстве случаев вы обнаружите, что sizeof(*x) фактически не оценивает *x вообще. И поскольку это оценка (удаление ссылок) указателя, вызывающего неопределенное поведение, вы обнаружите, что в основном это нормально. В стандарте C11 это сказано в 6.5.3.4. The sizeof operator /2 6.5.3.4. The sizeof operator /2 (мой акцент во всех этих цитатах):

Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражением или именем в скобках типа. Размер определяется по типу операнда. Результат – целое число. Если тип операнда – тип массива переменной длины, то операнд оценивается; в противном случае операнд не оценивается, а результат является целочисленной константой.

Это идентичная формулировка того же раздела в C99. У C89 была немного другая формулировка, потому что, конечно, в этот момент не было VLA. Из 3.3.3.4. The sizeof operator 3.3.3.4. The sizeof operator :

Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражением или именем в скобках типа. Размер определяется по типу операнда, который сам по себе не оценивается. Результат – целочисленная константа.

Таким образом, в C для всех не-VLA не происходит разыменования и утверждение корректно определено. Если тип *x является VLA, это считается *x фазы sizeof , то, что нужно разработать во время работы кода, – все остальные могут быть вычислены во время компиляции. Если сам x является VLA, это то же самое, что и в других случаях, при использовании *x в качестве аргумента для sizeof() не выполняется оценка.


C ++ имеет (как и ожидалось, поскольку это другой язык) несколько иные правила, как показано в различных итерациях стандарта:

Во-первых, C++03 5.3.3. Sizeof /1 C++03 5.3.3. Sizeof /1 :

Оператор sizeof дает количество байтов в представлении объекта своего операнда. Операнд – это либо выражение, которое не оценивается, либо идентификатор типа в скобках.

In, C++11 5.3.3. Sizeof /1 C++11 5.3.3. Sizeof /1 , вы найдете несколько другую формулировку, но тот же эффект:

Оператор sizeof дает количество байтов в представлении объекта своего операнда. Операндом является либо выражение, которое является неоцененным операндом (раздел 5), либо идентификатором типа в скобках.

C++11 5. Expressions /7 (упомянутый выше пункт 5) определяет термин «неоцененный операнд» как одну из самых бесполезных избыточных фраз, которые я читал некоторое время, но я не знаю, что происходит через сознание людей ИСО, когда они написали это:

В некоторых контекстах ( [некоторые ссылки на разделы, детализирующие эти контексты – pax] ) появляются неоцененные операнды. Неопределенный операнд не оценивается.

C ++ 14/17 имеют ту же формулировку, что и C ++ 11, но не обязательно в тех же разделах, поскольку материал был добавлен перед соответствующими частями. Они указаны в 5.3.3. Sizeof /1 5.3.3. Sizeof /1 и 5. Expressions /8 для C ++ 14 и 8.3.3. Sizeof /1 8.3.3. Sizeof /1 и 8. Expressions /8 для C ++ 17.

Таким образом, в C ++ оценка *x в sizeof(*x) никогда не происходит, поэтому она хорошо определена, если вы следуете всем остальным правилам, например, предоставляя полный тип. Но суть в том, что разыменование не выполняется, что означает, что это не вызывает проблемы.

Вы можете увидеть эту не-оценку в следующей программе:

 #include  #include  int main() { int x = 42; std::cout << x << '\n'; std::cout << sizeof(x = 6) << '\n'; std::cout << sizeof(x++) << '\n'; std::cout << sizeof(x = 15 * x * x + 7 * x - 12) << '\n'; std::cout << sizeof(x += sqrt(4.0)) << '\n'; std::cout << x << '\n'; } 

Вы могли бы подумать, что последняя строка выдаст что-то значительно отличное от 42 ( 774 , на основе моих грубых вычислений), потому что x было изменено совсем немного. Но на самом деле это не так, поскольку здесь имеет значение только тип выражения в sizeof , а тип сводится к типу x .

То, что вы видите (кроме возможности разных размеров указателя на линиях, отличных от первого и последнего), следующее:

 42 4 4 4 4 42 

sizeof является оператором и работает над типами, а не с фактическим значением (которое не оценивается).

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

 int* ptr = 0; size_t size = sizeof *ptr; size = sizeof (int); /* brackets still required when naming a type */ 

Ответ может быть различным для C, где sizeof не обязательно является конструкцией времени компиляции, но в C ++ выражение, предоставленное sizeof , никогда не оценивается. Таким образом, никогда не существует возможности для проявления неопределенного поведения. По аналогичной логике вы также можете «вызывать» функции, которые никогда не определены [потому что функция никогда не вызывается, определение не требуется], факт, который часто используется в правилах SFINAE.

sizeof и decltype не оценивают их операнды, только типы вычислений.

sizeof(*ptr) sizeof(int) в этом случае совпадает с sizeof(int) .

Поскольку sizeof не оценивает его операнд, в выражении sizeof (*ptr) , ptr не оценивается, поэтому он не разыменовывается. Оператору sizeof нужно только определить тип выражения *ptr чтобы получить соответствующий размер.

  • Является ли указатель с правильным адресом и типом все еще всегда действительным указателем с C ++ 17?
  • Являются ли примечания и примеры в спецификации основного языка Стандарта C ++ ненормативными?
  • c ++ доступ к статическим членам с использованием нулевого указателя
  • Является ли стандартный мандат преобразованием переменной lvalue-rval переменной указателя при применении косвенности?
  • Почему изменение строки через извлеченный указатель на его данные не разрешено?
  • Разница между неопределенным поведением и неправильной формой, не требуется диагностическое сообщение
  • Действительно ли «Неопределенное поведение» действительно позволяет * что-либо * произойти?
  • static_assert зависит от параметра шаблона непигового типа (различное поведение на gcc и clang)
  • Является ли это совместимым расширением компилятора для обработки стандартных библиотечных функций, отличных от constexpr, как constexpr?
  • Повторяющиеся typedefs - недопустимы в C, но действительны в C ++?
  • Вывод первого аргумента шаблона с другими параметрами шаблона по умолчанию
  • Interesting Posts

    как создать анимированный gif в .net

    Как я могу использовать SSH в «Bash on Ubuntu в Windows 10»?

    Каков фактический IP-адрес моего компьютера

    Мне нужно два экземпляра услуги AngularJS $ http или что?

    Java: В чем разница между автобоксингом и литьем?

    R / regex with stringi / ICU: почему символ «+» считается символом non – ?

    Почему мое правило Outlook 2007 не может быть серверным при использовании правила «Отметить как прочитанное»

    Перемещение файлов в локальной сети – сжатие улучшит скорость?

    self.delegate = self; что в этом плохого?

    Исключить свойство для обновления в инфраструктуре Entity

    Можете ли вы настроить log4net в коде вместо использования файла конфигурации?

    Не удалось получить доступ к определенному разделу реестра

    Будет ли мой USB-накопитель быстрее умирать, если я буду смотреть фильмы прямо из него?

    Захват стандартных ошибок и ошибок с помощью Start-Process

    Поставщик учетных данных Windows с C #

    Давайте будем гением компьютера.