Подтвердить что ты не робот

Может ли "sizeof" класс или объект когда-либо быть нулевым?

Мы все знаем, что sizeof пустого класса или объекта пустого класса будет 1 байт. Я столкнулся с чем-то, где sizeof класс, а его объект равен 0. Программа синтаксически корректна, поскольку не было ошибок компиляции или времени выполнения. Это поведение undefined? Случай использования, который я пытаюсь выполнить, имеет смысл и выглядит как действительный? Является ли большой ошибкой не давать точный индекс или размер массива в классе? Фрагмент кода выглядит следующим образом:

#include<iostream>
using namespace std;
class A
{
   char a[];
};
int main()
{
    A b;
    cout<<sizeof(A)<<endl;
    cout<<sizeof(b)<<endl;
    return 0;
}

выход:

0

0

sizeof пустой класс - это один байт (в основном не нулевой), и причина этого называется "Чтобы убедиться, что разные объекты имеют разные адреса".

Что произойдет в этом случае, когда класс sizeof будет равен нулю? Примечание. Наблюдается то же поведение для int a[].

4b9b3361

Ответ 1

Он называется "гибким элементом массива", и это особенность C99 (я думаю). Это недействительно С++ - у вас нет предупреждений/ошибок, возможно потому, что компилятор поддерживает его как расширение.

Компиляция с -Wall -Wextra -pedantic -std=c++NN (98, 03, 11, 14,..) должна генерировать предупреждение (последние два флага будут отключать любые расширения компилятора).


Вы можете увидеть некоторую информацию по этому связанному вопросу: Использует ли гибкие члены массива в C плохой практике?

Например, здесь GCC говорит об этом:

В ISO C99 вы должны использовать гибкий элемент массива, который немного отличается в синтаксисе и семантике:
...
Элементы гибкого массива имеют неполный тип, поэтому оператор sizeof не может быть применен. Как причуда исходной реализации массивов нулевой длины, sizeof оценивается до нуля.

(источник: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html).

Это объясняет размер 0 char a[], а не 0 для класса, но, как я уже упоминал, это функция C, а не допустимый С++.

Ответ 2

Если вы скомпилируете флаг -pedantic

$g++ -W -Wall -pedantic prog.cpp
prog.cpp: 5: 11: предупреждение: ISO С++ запрещает массив нулевого размера '[-pedantic]

С++ не поддерживает VLA, и поэтому объявление вашего класса не является законным и выходит за рамки стандартных правил С++.

Ответ 3

Ваш код не является стандартным С++, поэтому я не вижу в этом никакого смысла.

Если вы используете флаг педантизма, вы должны получить следующее:

[email protected]:~$ g++ -pedantic file.cpp
file.cpp:5:11: warning: ISO C++ forbids zero-size array ‘a’ [-Wpedantic]
    char a[];
         ^

Попробуйте изменить класс на

class A {
   char a[5];
};

тогда вы должны получить вывод

5
5

как и следовало ожидать.

Однако вы можете утверждать, что без флага ваш код компилирует и выводит нули. В качестве счетчика я мог бы сказать, что то же самое, если вы используете этот класс:

class A {
   char a[0];
};

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

Ответ 4

Пустые базовые классы могут быть оптимизированы до нулевых байтов, что технически сделает sizeof(base) также 0.

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

Итак:

struct base { };

struct derived : base { };

Оба sizeof(base) и sizeof(derived) разрешены 0, потому что объект derived - это тот же объект, что и объект base, содержащийся внутри.

Однако:

struct base1 { };
struct base2 { };
struct derived : base1, base2 { };

Здесь sizeof(derived) должно быть 1, поскольку для стандарта требуется, чтобы

derived d;
assert(static_cast<base1 *>(&d) != static_cast<base2 *>(&d));

Аналогично:

struct type1 { };
struct type2 { };
struct combined { type1 obj1; type2 obj2; };

требует, чтобы

combined c;
assert(&c.obj1 != &c.obj2);

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

Ответ 5

Размер класса может быть равен 0. Рассмотрим следующий фрагмент кода

#include <iostream>
using namespace std;

class A
{
    public:
    int a[0];
    void getA(){
        cout<<"Hello World";
    }
};

class B
{
};

int main()
{
    cout<<"The size of A is "<<sizeof(A)<<endl;   // prints 0
    A w;
    cout<<"The size of object of A is "<<sizeof(w)<<endl;    //prints 0
    cout<<"The size of the array a in A is "<<sizeof(w.a)<<endl;  // prints 0
    cout<<"The value from function of class A is  "<<w.getA()<<endl;  // Gives a compilation error
    cout<<"The size of B is "<<sizeof(B)<<endl;  //prints 1
}


Output:
The size of A is 0
The size of object of A is 0
The size of the array a in A is 0
The size of B is 1

Таким образом, доступ к функциям, присутствующим в классе с размером 0, приводит к ошибке компиляции.