2015-05-13 3 views
20

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

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

выход:

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

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

+8

'char a [];' Это не легальный синтаксис C++. – PaulMcKenzie

+1

«Программа синтаксически корректна, поскольку не было ошибок компиляции или времени выполнения.«Я уверен, что есть синтаксические ошибки« без необходимости диагностики ». – user2357112

+0

@PaulMcKenzie На самом деле, как последний член данных в структуре,' char a []; 'действителен C99. – fredoverflow

ответ

25

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

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


Вы можете увидеть некоторую информацию в этом связанный с этим вопрос: Is using flexible array members in C bad practice?

К примеру, вот что говорит НКУ об этом:

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

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

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

6

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

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

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

1

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

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

[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]; 
}; 

, но я уверен, что вы знаете, что нулевого размера массивы не допускается, но все-таки эта вещь отлично компилируется и дает выход нули.

-1

Пустые базовые классы могут быть оптимизированы до нулевых байтов, что технически сделает 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); 

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

+0

Я не уверен, что я следую. Я понимаю, что пустой 'производный' не должен содержать ничего, что' base' не является, и, следовательно, ему не нужно быть больше, чем 'base', но я бы подумал, что массив из десяти« базовых »экземпляров должен был бы занять десять различных, равно как и массив из десяти« выведенных »экземпляров. если 'производный' содержит' char', а также наследует 'base', я думаю, что адреса' base' instanc es должны отличаться от полей «char», хотя я не уверен в этом. – supercat

+0

Нет, это определенная оптимизация ([EBCO] (https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Empty_Base_Optimization)). –

+0

EBCO может быть разрешен, но, на мой взгляд, все это означает, что классу с пустой базой разрешено обрабатывать унаследованную базу, как если бы это было поле 'union', а не поле' struct' (хотя я любопытно, что это произойдет, если пустая база была передана путем ссылки на метод, который переписывал ее другим объектом базового типа. Я думаю, что нарезка определяется во многих случаях, и она будет вести себя разумно здесь, если базовый класс имеет свои собственные выделенные пространство, но я не вижу, как это может работать здесь). – supercat

-1

Размер класса может быть 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 приводит к ошибке компиляции.

+0

Ошибка компиляции заключается в том, что 'getA()' объявляется как возвращаемый 'void', но вы пытались его распечатать. –

+0

Да, я знаю. Я упомянул об этом в комментариях. – Rahul

Смежные вопросы