Nano Hash - криптовалюты, майнинг, программирование

Как эта структура может иметь sizeof == 0?

Есть старый пост с просьбой создать конструкцию, для которой sizeof вернет 0. Есть несколько ответов с высокими оценками от пользователей с высокой репутацией, в которых говорится, что по стандарту ни один тип или переменная не может иметь размер 0. И я согласен с этим на 100%.

Однако есть этот новый ответ, в котором представлено это решение:

struct ZeroMemory {
    int *a[0];
};

Я как раз собирался проголосовать и прокомментировать его, но время, проведенное здесь, научило меня проверять даже то, в чем я уверен на 100%. Итак... к моему удивлению, и gcc, и clang показывают одинаковые результаты: sizeof(ZeroMemory) == 0. Более того, размер переменной равен 0:

ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...

Чтоооо...?

ссылка на Godbolt

Как это возможно?


  • Массив нулевого размера не является стандартным С++. но расширение. 17.11.2017
  • @ Jarod42 теперь это очевидно. 17.11.2017
  • Также он не компилируется под MSVC 17.11.2017
  • Теперь поместите их в массив и выполните над ними арифметические операции с указателями. 17.11.2017
  • Хммм... Сколько байт ничего не должно занимать, если не 0? 17.11.2017
  • @Gerhardh Пустая структура имеет ненулевой размер в C/C++. 18.11.2017
  • @curiousguy Действительно ли имеет смысл придерживаться некоторых требований стандарта, если вы добавляете нестандартное расширение? Хотя sizeof==0 может быть нестандартным, именно этого я и ожидал от такого зверя. 18.11.2017
  • @curiousguy: стандарт C требует, чтобы структуры не были пустыми; он ничего не говорит о размере структур, нарушающих это ограничение. Поскольку пустые структуры имели смысл в C++, было необходимо указать, как они должны себя вести. Учитывая выбор между разрешением объектам иметь нулевой размер или требованием, чтобы пустые структуры имели ненулевой размер, дизайн С++ (к сожалению, ИМХО) выбрал последний подход. 21.11.2017

Ответы:


1

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

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

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

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

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

Стандарт C++ допускает существование пустых объектов, но для того, чтобы адреса пустых объектов можно было использовать в качестве токенов, он предписывает, чтобы они имели минимальный размер 1. Для объекта, который не имеет членов, должен иметь размер 0, таким образом, нарушил бы Стандарт. Однако, если объект содержит элементы нулевого размера, стандарт C++ не предъявляет никаких требований к тому, как он обрабатывается, за исключением того факта, что программа, содержащая такое объявление, должна запускать диагностику. Поскольку большая часть кода, использующего такие объявления, ожидает, что результирующие объекты будут иметь нулевой размер, наиболее полезным поведением для компиляторов, получающих такой код, будет обработка их таким образом.

17.11.2017
  • До стандартизации C, вы имеете в виду до появления C99? 21.11.2017
  • @Stargateur: я имею в виду примерно 15 лет между изобретением C и написанием стандарта C89. C99 добавил обратно ограниченную форму объекта нулевого размера, чтобы избежать некоторых кладжей, необходимых для обхода запрета на массивы нулевого размера, но в таких кладжах не было бы необходимости, если бы C89 не раздражал запретом массивов нулевого размера в первое место. 21.11.2017
  • Такие типы были полезны Чем они были полезны? 12.07.2019
  • @ user109923: Например: struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };. Нужно соблюдать осторожность, чтобы выравнивание не вызывало проблем, но могут быть объекты статической продолжительности разного размера, которые можно обрабатывать взаимозаменяемо. 12.07.2019

  • 2

    Как указано Jarod42, массивы нулевого размера не являются стандартными C++, а являются расширениями GCC и Clang.

    Добавление -pedantic приводит к следующему предупреждению:

    5 : <source>:5:12: warning: zero size arrays are an extension [-Wzero-length-array]
        int *a[0];
               ^
    

    Я всегда забываю, что std=c++XX (вместо std=gnu++XX) не отключает все расширения.

    Это все еще не объясняет поведение sizeof. Но, по крайней мере, мы знаем, что это не стандарт...

    17.11.2017
  • В любом случае это удивительно, поскольку даже пустая структура должна давать ненулевое значение sizeof... 17.11.2017
  • да, и еще более удивительно для меня то, что переменная sizeof равна 0: ZeroMemory z{}; sizeof(z) == 0 17.11.2017
  • Интересно, что если вы создадите два автоматических экземпляра, они будут храниться на расстоянии sizeof(int*) друг от друга (я предполагаю): -crooked.com/a/e9a3038bf587b65c 17.11.2017
  • Это только отвечает на то, что массив нулевого размера не является стандартным, но не объясняет заданный вами вопрос. 17.11.2017
  • @hacks Как это не так? В C++ невозможно иметь данные нулевого размера, но C++ с расширениями больше не является C++, поэтому головоломка исчезла. 17.11.2017
  • Поведение не обязательно должно иметь смысл в соответствии со стандартом — оно должно быть понятно только разработчикам компилятора. Компилятор уже позволяет определять тип способом, запрещенным стандартом. Таким образом, требования стандарта о том, что дает результат sizeof для этого типа, также не применяются. Таким образом, поведение — это решение, принятое разработчиками компилятора. 18.11.2017
  • Идея ZeroMemory* z = (ZeroMemory*)malloc(sizeof(ZeroMemory) + 2 * sizeof(int*)); z.a[1] = new int{1}; /* or whatever, just use indices into a to access dynamic memory after the previous members (in this case: none) */ Конечно, это несовместимо со стандартом! (Это можно использовать для простого анализа сетевых сообщений динамической длины, например, путем приведения к структуре). 18.11.2017

  • 3

    В C++ массив нулевого размера недопустим.

    ИСО/МЭК 14882:2003 8.3.4/1:

    [..] Если присутствует константное-выражение (5.19), это должно быть целочисленное константное выражение, и его значение должно быть больше нуля. Постоянное выражение указывает границу (количество элементов) массива. Если значение константного выражения равно N, массив состоит из N элементов, пронумерованных от 0 до N-1, а тип идентификатора D — «массив производный-список-объявлений из N T». [..]

    g++ требует, чтобы флаг -pedantic выдавал предупреждение о массиве нулевого размера.

    17.11.2017

    4

    Массивы нулевой длины являются расширением GCC и Clang. Применение sizeof к массивам нулевой длины приводит к нулю.

    Класс C++ (пустой) не может иметь размер 0, но обратите внимание, что класс ZeroMemory не пуст. У него есть именованный член с размером 0, и применение sizeof вернет ноль.

    17.11.2017
  • Итак... пустой класс не может иметь размер 0, но непустой класс может иметь размер 0... это не имеет особого смысла. Но я предполагаю, что это своего рода конфликтный пограничный случай, который вы получаете с нестандартными расширениями. 17.11.2017
  • @болов; Класс c++ (пустой) не может иметь размер 0 в соответствии со стандартом для пустого класса. В стандарте С++ сказано, что нет другого способа создать структуру размером 0. В этом конкретном случае член самой структуры имеет размер 0, и это делает размер структуры равным 0. Я знаю, что это сбивает с толку, но я старался изо всех сил объяснить. 17.11.2017
  • @bodov: Но это не класс C++, это класс G++, поэтому правила C++ не должны применяться к нему. Особо обратите внимание, что вы не можете иметь массив такого типа или выполнять математические операции с его указателями, поэтому обычное рассуждение о том, что размер не может быть равен нулю, также не работает. 18.11.2017
  • Новые материалы

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

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..