4

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;
}        
Padawan
  • 313
  • 5
  • 21
SAC
  • 243
  • 3
  • 13

4 Answers4

8

Your class definition is illegal. C++ does not allow array declarations with size 0 in any context. But even if you make your class definition completely empty, the sizeof is still required to evaluate to a non-zero value.

9/4 Complete objects and member subobjects of class type shall have nonzero size.

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. It must be a compiler extension that has no relation to standard 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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    Out of curiosity, what's the result on your platform? because [here](http://coliru.stacked-crooked.com/view?id=4752695d0f) and [here](http://ideone.com/L61cWQ) it prints `0` (and `Fine` too)... This really looks like undefined behavior – gx_ Aug 31 '13 at 18:10
  • Of course, what it actually prints will vary from compiler to compiler. – Billy ONeal Aug 31 '13 at 18:10
  • @gx_: Sorry, I made my experiments with commented-out array declaration, meaning that it does not apply to the original code. I removed it from the answer. – AnT stands with Russia Aug 31 '13 at 18:15
  • "C++ does not allow arrays of size 0 in any context." For completeness: for "statically allocated" arrays indeed; but one could consider `new int[0]` (which is valid) a "dynamically allocated array". – gx_ Aug 31 '13 at 18:16
  • 1
    @AndreyT All extensions are required to be documented, but a compiler is allowed to not support zero-length arrays at all, yet not issue a fatal error when encountering them (for example, if the detection has known false positives for non-zero array lengths -- yes, that can happen). In that case, the behaviour is undefined, not implementation-defined. –  Aug 31 '13 at 18:25
  • @AndreyT My bad, you're totally right, "ISO C++ forbids zero-size array", it shouldn't even compile. The problem is that I can't manage to get an error instead of a warning on GCC... – gx_ Aug 31 '13 at 18:25
  • @hvd Indeed! Thank you =) (also related: http://stackoverflow.com/questions/10353240/array-of-zero-size ) – gx_ Aug 31 '13 at 18:30
6

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.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 2
    Quoting the C++11 Standard, §8.3.4 [dcl.array] "[the value of the integral constant expression used to specify the _bound_ of the array in its declaration] shall be greater than zero." – gx_ Aug 31 '13 at 18:20
4

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++.

The ISO C++11 standard, in section 8.3.4 [dcl.array], paragraph 1, says:

If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero.

Your class definition:

class Test
{
    int arr[0];
};

violates this rule. Section 1.4 [intro.compliance] applies here:

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:

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

(Apparently you're also using g++.)

Experiment shows that g++'s treatment of zero-sized arrays is a bit odd. 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];, 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. g++ follows this requirement for class Empty, which has no members. But adding a zero-sized array to a class actually causes the class itself to have a size of 0.

If you declare two objects of type Almost_Empty, they have distinct addresses, which is sensible; 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.

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.

This does not indicate that g++ fails to conform to the C++ standard. 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++. 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++. 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. It's replaced in C99 by flexible array members, and in C++ by container classes.

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • It's not necessarily a bug. [The description of this feature](http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html) states that zero-sized arrays *were* used as flexible array members. To use them, you'd either statically initialize the object, or, more commonly, use `malloc(sizeof(Almost_Empty) + n*sizeof(int))`. As the struct is empty, you shouldn't have to allocate another (unused) byte here. (Note C99 doesn't allow flexible array members of otherwise empty structs.) – dyp Aug 31 '13 at 20:04
  • Btw "adding an empty array to a class should not make the class bigger." Typo? smaller? – dyp Aug 31 '13 at 20:05
1
  • 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.
  • 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.
  • 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. 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.
Juniar
  • 1,269
  • 1
  • 15
  • 24
  • You have no idea what you are talking about. – Blastfurnace Aug 31 '13 at 21:08
  • well, only one who knows the difference between declaration and definition, for one. somebody should find the sizeof function (a macro, no?) just to see if it doesn't do what everybody seems to think it does... – Dru Sep 01 '13 at 04:25