简体   繁体   English

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

[英]Why is the size of my class zero? How can I ensure that different objects have different address?

I created a class but its size is zero. 我创建了一个类,但它的大小为零。 Now, how can I be sure that all objects have different addresses? 现在,我怎样才能确定所有对象都有不同的地址? (As we know, empty classes have a non-zero size.) (我们知道,空类的大小不为零。)

#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;
}        

Your class definition is illegal. 你的班级定义是非法的。 C++ does not allow array declarations with size 0 in any context. C ++不允许在任何上下文中使用大小为0数组声明。 But even if you make your class definition completely empty, the sizeof is still required to evaluate to a non-zero value. 但即使您将类定义完全清空,仍需要sizeof来计算非零值。

9/4 Complete objects and member subobjects of class type shall have nonzero size. 9/4类类型的完整对象和成员子对象应具有非零大小。

In other words, if your compiler accepts the class definition and evaluates the above sizeof to zero, that compiler is going outside of scope of standard C++ language. 换句话说,如果编译器接受类定义并将上面的sizeof计算为零,那么该编译器将超出标准C ++语言的范围。 It must be a compiler extension that has no relation to standard C++. 它必须是与标准C ++无关的编译器扩展。

So, the only answer to the "why" question in this case is: because that's the way it is implemented in your compiler. 因此,在这种情况下,“为什么”问题的唯一答案是:因为这是在编译器中实现的方式。

I don't see what it all has to do with ensuring that different objects have different addresses. 我不知道它与确保不同对象具有不同地址有什么关系。 The compiler can easily enforce this regardless of whether object size is zero or not. 无论对象大小是否为零,编译器都可以轻松地强制执行此操作。

The standard says that having an array of zero size causes undefined behavior. 该标准表示具有零大小的数组会导致未定义的行为。 When you trigger undefined behavior, other guarantees that the standard provides, such as requiring that objects be located at a different address, may not hold. 当您触发未定义的行为时,标准提供的其他保证(例如要求对象位于不同的地址)可能不成立。

Don't create arrays of zero size, and you shouldn't have this problem. 不要创建零大小的数组,并且不应该有这个问题。

This is largely a repetition of what the other answers have already said, but with a few more references to the ISO C++ standard and some musings about the odd behavior of g++. 这在很大程度上是对其他答案已经说过的重复,但是有一些对ISO C ++标准的引用以及关于g ++奇怪行为的一些思考。

The ISO C++11 standard, in section 8.3.4 [dcl.array], paragraph 1, says: ISO C ++ 11标准,在第8.3.4节[dcl.array]第1段中说:

If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. 如果存在常量表达式 (5.19),则它应为整数常量表达式,其值应大于零。

Your class definition: 你的班级定义:

class Test
{
    int arr[0];
};

violates this rule. 违反了这条规则。 Section 1.4 [intro.compliance] applies here: 第1.4节[intro.compliance]适用于此:

If a program contains a violation of any diagnosable rule [...], a conforming implementation shall issue at least one diagnostic message. 如果程序包含违反任何可诊断规则的情况,则符合要求的实施方案应至少发出一条诊断消息。

As I understand it, if a compiler issues this diagnostic and then accepts the program, the program's behavior is undefined. 据我了解,如果编译器发出此诊断信息然后接受该程序,则程序的行为是未定义的。 So that's all the standard has to say about your program. 所以这是关于你的计划的所有标准。

Now it becomes a question about your compiler rather than about the language. 现在它成了关于编译器而不是语言的问题。

I'm using g++ version 4.7.2, which does permit zero-sized arrays as an extension, but prints the required diagnostic (a warning) if you invoke it with, for example, -std=c++11 -pedantic : 我正在使用g ++版本4.7.2,它允许零大小的数组作为扩展,但是如果你使用例如-std=c++11 -pedantic调用它,则打印所需的诊断(警告):

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

(Apparently you're also using g++.) (显然你也在使用g ++。)

Experiment shows that g++'s treatment of zero-sized arrays is a bit odd. 实验表明,g ++对零大小数组的处理有点奇怪。 Here's an example, based on the one in your program: 这是一个基于程序中的例子:

#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";
    }
}

I get the required warning on int arr[0]; 我在int arr[0];上得到了必要的警告int arr[0]; , and then the following run-time output: ,然后是以下运行时输出:

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

C++ requires a class, even one with no members, to have a size of at least 1 byte. C ++要求一个类,即使没有成员的类,也要有至少1个字节的大小。 g++ follows this requirement for class Empty , which has no members. 对于没有成员的类Empty ,遵循此要求。 But adding a zero-sized array to a class actually causes the class itself to have a size of 0. 但是为类添加一个零大小的数组实际上会导致类本身的大小为0。

If you declare two objects of type Almost_Empty , they have distinct addresses, which is sensible; 如果你声明两个Almost_Empty类型的Almost_Empty ,它们有不同的地址,这是明智的; the compiler can allocate distinct objects any way it likes. 编译器可以按照自己喜欢的方式分配不同的对象。

But for elements in an array, a compiler has less flexibility: an array of N elements must have a size of N times the number of elements. 但是对于数组中的元素,编译器的灵活性较小:N个元素的数组必须具有N倍元素的大小。

In this case, since class Almost_Empty has a size of 0, it follows that an array of Almost_Empty elements has a size of 0 *and that all elements of such an array have the same address. 在这种情况下,由于类Almost_Empty的大小为0,因此Almost_Empty元素的数组大小为0 *,并且这种数组的所有元素具有相同的地址。

This does not indicate that g++ fails to conform to the C++ standard. 这并不表示g ++不符合C ++标准。 It's done its job by printing a diagnostic (even though it's a non-fatal warning); 它通过打印诊断来完成它的工作(即使它是一个非致命的警告); after that, as far as the standard is concerned, it's free to do whatever it likes. 在此之后,就标准而言,它可以自由地做任何它喜欢的事情。

But I would probably argue that it's a bug in g++. 但我可能会说这是g ++中的一个错误。 Just in terms of common sense, adding an empty array to a class should not make the class smaller. 就常识而言,向类添加空数组不应该使类变小。

But there is a rationale for it. 但它有一个理由。 As DyP points out in a comment, the gcc manual (which covers g++) mentions this feature as a C extension which is also available for C++ . 正如DyP在评论中指出的那样,gcc手册(包含g ++) 提到这个功能是一个C扩展, 也可用于C ++ They are intended primarily to be used as the last member of a structure that's really a header for a variable-length object. 它们主要用作结构的最后一个成员,它实际上是一个可变长度对象的头。 This is known as the struct hack . 这被称为struct hack It's replaced in C99 by flexible array members, and in C++ by container classes. 它在C99中由灵活的数组成员替换,在C ++中由容器类替换。

My advice: Avoid all this confusion by not defining zero-length arrays. 我的建议:通过不定义零长度数组来避免所有这些混淆。 If you really need sequences of elements that can be empty, use one of the C++ standard container classes such as std::vector or std::array . 如果您确实需要可以为空的元素序列,请使用其中一个C ++标准容器类,例如std::vectorstd::array

  • There is a difference between variable declaration and variable initialization. 变量声明和变量初始化之间存在差异。 In your case, you just declare variables; 在您的情况下,您只需声明变量; A and B. Once you have declared a variable, you need to initialize it using either NEW or MALLOC. A和B.一旦声明了变量,就需要使用NEW或MALLOC对其进行初始化。
  • The initialization will now allocate memory to the variables that you just declared. 初始化现在将为您刚刚声明的变量分配内存。 You can initialize the variable to an arbitrary size or block of memory. 您可以将变量初始化为任意大小或内存块。
  • A and B are both variables meaning you have created two variables A and B. The compiler will identify this variable as unique variables, it will then allocate A to a memory address say 2000 and then allocate B to another memory address say 150. A和B都是变量,意味着你创建了两个变量A和B.编译器将此变量标识为唯一变量,然后将A分配给内存地址,例如2000,然后将B分配给另一个内存地址,例如150。
  • If you want A to point to B or B to point to A, you can make a reference to A or B such as; 如果您希望A指向B或B指向A,您可以引用A或B,例如; A = &B. A =&B。 Now A as a memory reference or address to B or rather A points to B. This is called passing variables, in C++ you can either pass variables by reference or pass variables by value. 现在A作为B的内存引用或地址,或者更确切地说A指向B.这称为传递变量,在C ++中,您可以通过引用传递变量或按值传递变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 两个子类具有相同的代码,但基类不同。 如何确保重用? - Two subclasses have the same code but different baseclasses. How can I ensure reuse? 如何让一组不同的子类对象共享另一组的基类的静态变量? - How can I have a group of different child class objects sharing one static variable of the base class from another group? 类函数是否可以具有作为不同类对象的参数? - Can Class Functions Have Parameters That Are Different Class Objects? 在我的情况下,如何重载 equal 方法以使不同的对象在 unordered_multimap 中具有相同的哈希码值 - How can I overload equal method to make different objects have same hashcode value in unordered_multimap in my case 为什么基类的大小为零? - Why base classes can have a zero size? 如何为数组分配其他地址? - How can I assign a different address to an array? 如何在其他文件中创建班级? - How can I create my class in different file? 为什么 CMake 会编译不同大小的对象? - Why CMake compiles objects with different size? 跨模块(DLL)的类大小不同。 如何以及为什么? - Class size is different across modules (DLLs). How and why? 如何使用 for 循环从包含不同派生类的基本 class 数组初始化对象? - How can I initialize objects from base class array, containing different derived classes, using a for loop?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM