一个类或对象的“sizeof”可以为零吗?

Posted

技术标签:

【中文标题】一个类或对象的“sizeof”可以为零吗?【英文标题】:Can "sizeof" a class or object ever be zero? 【发布时间】:2015-07-24 12:48:39 【问题描述】:

我们都知道空类或空类对象的大小为1字节。 我遇到了 sizeof 一个类及其对象为 0 的情况。该程序在语法上是正确的,因为没有编译或运行时错误。这是未定义的行为吗?我试图执行的用例是否有意义并且看起来像一个有效的用例?不为类中的数组提供确切的下标或大小是否是一个大错误?代码sn-p如下:

#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[] 也观察到相同的行为。

【问题讨论】:

char a[]; 这不是合法的 C++ 语法。 "程序在语法上是正确的,因为没有编译或运行时错误。" - 我很确定存在“不需要诊断”语法错误。 @PaulMcKenzie 实际上,作为结构中的最后一个数据成员,char a[]; 是有效的 C99。 @fredoverflow:C++ 不是 C99。 C++ 基本规则见***.com/questions/621616/… 【参考方案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 的函数会导致编译错误。

【讨论】:

编译错误是因为getA()被声明返回void,但是你试图打印它。 是的,我知道。我确实在 cmets 中提到过。【参考方案2】:

如果你用-pedantic 标志编译

$ g++ -W -Wall -pedantic prog.cpp prog.cpp:5:11: 警告:ISO C++ 禁止零大小数组'a' [-pedantic]

C++ 不支持VLAs,因此您的类声明是不合法的并且超出了标准 C++ 规则的范围。

【讨论】:

【参考方案3】:

可以将空基类优化为零字节,从技术上讲,这将使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);

许多编译器供应商采用捷径,只是让空类占用一个字节。

【讨论】:

我不确定我是否遵循。我知道空的derived 不需要包含base 不包含的任何内容,因此不需要大于base,但我认为一个包含十个base 实例的数组需要占用十个不同的,十个 derived 实例的数组也是如此。即使derived 包含char 以及继承base,我认为base 实例的地址也需要与char 字段的地址不同,尽管我对此并不肯定. 不,这是特别允许的优化 (EBCO)。 EBCO 可能是允许的,但在我看来,这意味着允许具有空基的类将继承的基视为union 字段而不是@987654346 @field(虽然我很好奇如果一个空基通过引用传递给一个用另一个基类型对象覆盖它的方法会发生什么;我认为切片在许多情况下是定义的行为,如果基类有自己分配的空间,但我看不出它在这里如何工作)。 sizeof(base)sizeof(derived) 都允许为 0” 不,它们不是。但是,sizeof(derived) == sizeof(base) 是允许的,对于struct derived : base int m; ;sizeof(derived) == sizeof(int) 是允许的。虽然任何完整对象必须占用地址空间(因为唯一的地址),但基类子对象不是完整对象,因此derived d; (void*)&amp;d == (void*)(base*)&amp;b是允许的。 [intro.object]p5 “除非它是位域,否则最衍生的对象应具有非零大小并应占用一个或多个字节的存储空间。”【参考方案4】:

它被称为“灵活数组成员”,它是 C99 的一个特性(我认为)。它不是有效的 C++ - 你没有警告/错误,可能是因为编译器支持它作为扩展。

使用-Wall -Wextra -pedantic -std=c++NN (98, 03, 11, 14, ..) 编译应该会产生警告(最后两个标志将禁用任何编译器扩展)。


你可以在这个相关问题中看到一些信息:Is using flexible array members in C bad practice?

例如,GCC 对此是这么说的:

在 ISO C99 中,您将使用灵活的数组成员,它在语法和语义上略有不同: ... 灵活数组成员的类型不完整,因此可能无法应用 sizeof 运算符。 作为零长度数组的原始实现的一个怪癖,sizeof 的计算结果为零

(来源:https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html)。

这解释了 char a[]0 大小而不是类的 0,但正如我已经提到的 - 它是 C 功能而不是有效的 C++。

【讨论】:

【参考方案5】:

您的代码不是标准 C++,因此我看不出有任何意义。

如果你使用pedantic flag,你应该收到这个:

gsamaras@pythagoras:~$ 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];
;

但是我很确定你知道零大小的数组是不允许的,但是这个东西仍然可以很好地编译并且输出为零。

【讨论】:

以上是关于一个类或对象的“sizeof”可以为零吗?的主要内容,如果未能解决你的问题,请参考以下文章

基于原子操作的自旋锁的 Unlock 可以直接将锁标志设置为零吗?

iOS:openURL 可以为零吗?

在 POSIX 系统上 argc 可以为零吗?

可以在不调用 memset 的情况下从构造函数初始化器列表中将成员结构设为零吗?

为啥这不会崩溃?我不是在这里除以零吗?

使用 C TCP 套接字,“发送”可以返回零吗?