C++ Can";sizeof“;类或对象是否曾经为零?

C++ Can";sizeof“;类或对象是否曾经为零?,c++,class,object,C++,Class,Object,我们都知道空类或空类对象的大小将是1字节。 我遇到了一个类,它的对象是0。程序语法正确,因为没有编译或运行时错误。这是未定义的行为吗?我试图执行的用例有任何意义,并且看起来是有效的?不给类中的数组提供精确的下标或大小是一个大错误吗?代码片段如下所示: #include<iostream> using namespace std; class A { char a[]; }; int main() { A b; cout<<sizeof(A)<&

我们都知道空类或空类对象的大小将是1字节。
我遇到了一个类,它的对象是0。程序语法正确,因为没有编译或运行时错误。这是未定义的行为吗?我试图执行的用例有任何意义,并且看起来是有效的?不给类中的数组提供精确的下标或大小是一个大错误吗?代码片段如下所示:

#include<iostream>
using namespace std;
class A
{
   char a[];
};
int main()
{
    A b;
    cout<<sizeof(A)<<endl;
    cout<<sizeof(b)<<endl;
    return 0;
}
#包括
使用名称空间std;
甲级
{
字符a[];
};
int main()
{
A b;

如果使用
-pedantic
标志编译,则不能

+g++-W-墙壁学前程序CPP
PRG.CPP:5:11:警告:ISO C++ 禁止零大小数组“a”[-pedantic]


C++不支持S,因此你的类声明不合法,超出了标准C++规则的范围。

< p>你的代码不是标准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];
};
<>但是我确信你不知道零大小的数组,但是这个东西仍然编译得很好,并且给出了0的输出。

它被称为“灵活数组成员”,它是C99的一个特性(我想)。它不是有效的C++——你没有警告/错误,可能是因为编译器支持它作为扩展。< /P> 使用
-Wall-Wextra-pedantic-std=c++NN(98,03,11,14,…)编译时应生成警告(最后两个标志将禁用任何编译器扩展)


您可以在此相关问题中看到一些信息:

例如,GCC是这样说的:

在ISO C99中,您将使用灵活的数组成员,这在语法和语义上略有不同:

灵活数组成员的类型不完整,因此可能无法应用sizeof运算符。作为零长度数组原始实现的一个怪癖,sizeof的计算结果为零

(来源:)


这解释了<代码> 0 > <代码> [A]< /C> >而不是0,但我已经提到了,它是C特性而不是有效的C++。

< P>空基类可以被优化为零字节,这将在技术上使<代码> siZeof(Base)< /C> >也>代码> 0 “1字节”实际上是一个实现细节,来自不同对象需要有不同地址的规则

因此:

sizeof(base)
sizeof(derived)
都允许为
0
,因为
派生的
对象与中包含的
基本
对象是同一个对象

然而:

struct base1 { };
struct base2 { };
struct derived : base1, base2 { };
这里,
sizeof(派生)
必须为1,因为标准要求

derived d;
assert(static_cast<base1 *>(&d) != static_cast<base2 *>(&d));
combined c;
assert(&c.obj1 != &c.obj2);
要求

derived d;
assert(static_cast<base1 *>(&d) != static_cast<base2 *>(&d));
combined c;
assert(&c.obj1 != &c.obj2);

许多编译器厂商采用快捷方式,只让空类占用一个字节。

< P>类的大小可以是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
#包括
使用名称空间std;
甲级
{
公众:
int a[0];
void getA(){

Cux< Calp> Car A[];这不是合法的C++语法。“程序在语法上是正确的,因为没有编译或运行时错误。”-我很确定,“没有诊断需要的”语法错误。@ PulcMcKeZeIn实际上是结构中的最后一个数据成员,<代码> char A[]。“<代码>”是有效的C99。@ FrdodoFLUV:C++不是C99。对于基本的C++规则,我不确定我是否遵循。我理解空的<代码>派生的<代码>不需要包含任何代码< < BASE>代码> >,因此不需要比<代码> BASE<代码>更大,但是我认为十个代码> BASE> <代码>实例将是n个数组。需要占用十个不同的,就像一个包含十个
派生
实例的数组一样。即使
派生
包含一个
字符以及继承
,我认为
实例的地址需要与
字符
字段的地址不同,尽管我对此不是肯定的。不,t这是一个特别允许的优化()。EBCO可能是允许的,但在我看来,这意味着允许一个基址为空的类将继承的基址当作
联合
字段而不是
结构
字段来处理(虽然我很好奇,如果通过引用一个空基来传递一个用另一个基类型的对象重写它的方法,会发生什么情况;我认为切片在很多情况下是定义行为,如果基类有自己分配的空间,它在这里的行为会正常,但我不知道它在这里是如何工作的)。”
sizeof(派生)
允许为0“不,它们不是。但是,
sizeof(派生)==sizeof(基)
是允许的,对于
struct-derived:base{int m;};
sizeof(派生)==sizeof(int)
也是允许的。而任何完整的对象都必须占用地址空间(因为地址唯一),基类子对象不是完整的对象,因此允许
派生的d;(void*)&d==(void*)(base*)&b
。[intro.object]p5“除非是位字段,最派生的对象应该具有非零大小,并且应该占用一个或多个字节的存储。”编译错误是因为
getA()
声明返回
void
,但您试图打印它。是的,我知道。我在评论中提到过。