为什么班级的大小为零?如何确保不同的对象具有不同的地址?

时间:2013-08-31 17:56:07

标签: c++ class

我创建了一个类,但它的大小为零。现在,我怎样才能确定所有对象都有不同的地址? (我们知道,空类的大小不为零。)

#include<cstdio>
#include<iostream>
using namespace std;
class Test
{
    int arr[0];//Why is the sizezero?
};

int main()
{
    Test a,b;  
      cout <<"size of class"<<sizeof(a)<<endl;
       if (&a == &b)// now how we ensure about address of objects ?
          cout << "impossible " << endl;
       else
          cout << "Fine " << endl;//Why isn't the address the same? 

        return 0;
}        

4 个答案:

答案 0 :(得分:8)

您的班级定义是非法的。 C ++不允许在任何上下文中使用大小为0的数组声明。但即使您将类定义完全清空,仍然需要sizeof来计算非零值。

  

9/4类类型的完整对象和成员子对象应具有   非零大小。

换句话说,如果您的编译器接受类定义并将上面的sizeof计算为零,那么该编译器将超出标准C ++语言的范围。它必须是与标准C ++无关的编译器扩展。

因此,在这种情况下,“为什么”问题的唯一答案是:因为这是在编译器中实现的方式。

我不知道与确保不同对象具有不同地址有什么关系。无论对象大小是否为零,编译器都可以轻松地强制执行此操作。

答案 1 :(得分:6)

标准规定,拥有零大小的数组会导致未定义的行为。当您触发未定义的行为时,标准提供的其他保证(例如要求对象位于不同的地址)可能无法保留。

不要创建零大小的数组,并且不应该有这个问题。

答案 2 :(得分:4)

这在很大程度上是对其他答案已经说过的重复,但是有一些对ISO C ++标准的引用以及关于g ++奇怪行为的一些思考。

ISO C ++ 11标准,在第8.3.4节[dcl.array]第1段中说:

  

如果存在常量表达式(5.19),则它应为a   积分常数表达式及其值应大于零。

您的班级定义:

class Test
{
    int arr[0];
};

违反此规则。第1.4节[intro.compliance]适用于此:

  

如果程序包含违反任何可诊断规则的行为,...   符合要求的实施应至少发布一条诊断信息。

据我了解,如果编译器发出此诊断信息然后接受该程序,则程序的行为是未定义的。所以这是关于你的计划的所有标准。

现在它成了关于编译器的问题,而不是关于语言的问题。

我正在使用g ++版本4.7.2,它允许将零大小的数组作为扩展名,但如果您使用-std=c++11 -pedantic调用它,则会打印所需的诊断(警告):

warning: ISO C++ forbids zero-size array ‘arr’ [-pedantic]

(显然你也在使用g ++。)

实验表明,g ++对零大小数组的处理有点奇怪。这是一个基于程序中的例子的例子:

#include <iostream>

class Empty {
    /* This is valid C++ */
};

class Almost_Empty {
    int arr[0];
};

int main() {
    Almost_Empty arr[2];
    Almost_Empty x, y;

    std::cout << "sizeof (Empty)        = " << sizeof (Empty) << "\n";
    std::cout << "sizeof (Almost_Empty) = " << sizeof (Almost_Empty) << "\n";
    std::cout << "sizeof arr[0]         = " << sizeof arr[0] << '\n';
    std::cout << "sizeof arr            = " << sizeof arr << '\n';

    if (&x == &y) {
        std::cout << "&x == &y\n";
    }
    else {
        std::cout << "&x != &y\n";
    }

    if (&arr[0] == &arr[1]) {
        std::cout << "&arr[0] == &arr[1]\n";
    }
    else {
        std::cout << "&arr[0] != &arr[1]\n";
    }
}

我在int arr[0];上收到了所需的警告,然后是以下运行时输出:

sizeof (Empty)        = 1
sizeof (Almost_Empty) = 0
sizeof arr[0]         = 0
sizeof arr            = 0
&x != &y
&arr[0] == &arr[1]

C ++要求一个类(即使没有成员的类)的大小至少为1个字节。 g ++遵循此类Empty的要求,该类没有成员。但是为类添加一个零大小的数组实际上会导致类本身的大小为0。

如果声明两个类型为Almost_Empty的对象,则它们具有不同的地址,这是明智的;编译器可以按照自己喜欢的方式分配不同的对象。

但对于数组中的元素,编译器的灵活性较低:N个元素的数组必须的大小为元素数的N倍。

在这种情况下,由于类Almost_Empty的大小为0,因此Almost_Empty元素的数组大小为0 *,并且此类数组的所有元素具有相同的地址

这并不表示g ++不符合C ++标准。它通过打印诊断来完成它的工作(即使它是一个非致命的警告);之后,就标准而言,它可以随意做任何事情。

但我可能会认为这是g ++中的一个错误。就常识而言,在类中添加一个空数组不应该使类变小。

但是有一个理由。正如DyP在评论中指出的那样,gcc手册(包含g ++)mentions this feature作为C扩展名also available for C++。它们主要用作结构的最后一个成员,它实际上是一个可变长度对象的头。这被称为struct hack。它在C99中由灵活的数组成员替换,在C ++中由容器类替换。

我的建议:不要定义零长度数组,避免所有这些混乱。如果您确实需要可以为空的元素序列,请使用其中一个C ++标准容器类,例如std::vectorstd::array

答案 3 :(得分:1)

  • 变量声明和变量初始化之间存在差异。在您的情况下,您只需声明变量; A和B.一旦声明了变量,就需要使用NEW或MALLOC初始化它。
  • 现在,初始化将为您刚刚声明的变量分配内存。您可以将变量初始化为任意大小或内存块。
  • A和B都是变量,意味着你创建了两个变量A和B.编译器将此变量标识为唯一变量,然后将A分配给内存地址,例如2000,然后将B分配给另一个内存地址,例如150 。
  • 如果您希望A指向B或B指向A,您可以引用A或B,例如; A =&amp; B。现在A作为B的内存引用或地址,或者更确切地说A指向B.这称为传递变量,在C ++中,您可以通过引用传递变量或按值传递变量。