Может ли "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[]
.
Ответы
Ответ 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, приводит к ошибке компиляции.