Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Zero-Length Arrays in C++

#include <stdlib.h>

void *operator new[](size_t size, int n){
    if( size != 0 && n != 0 )
        return calloc(n, size);
    return calloc(1, 1);
}

int main(){

    int * p1;
    const int i = 0;

//  p1 = new (20)  int[i] ; // Case 1 (OK)
    p1 = new (20) (int[i]); // Case 2 (Warning)

    if( p1 == 0 )
        return 1;
    return 0;
}

This code (https://godbolt.org/g/hjo7Xn) compiles successfully with Clang 6.0.0, however, GCC 7.3 issues a warning saying that zero-length arrays are forbidden in C++. If the parentheses are removed (Case 1), the warning goes away.

Unlike statically allocated zero-length arrays (C++03:8.3.4/1), dynamically allocated zero-length arrays are allowed (C++03:5.3.4/6). Nevertheless, in the C++ Standard the latter are explicitly allowed only when following one of the two possible syntax paths of the new-expression, that is, the one with new-type-id and without parentheses (Case 1).

Is it allowed by the C++ Standard to use the new-expression with a zero-length array following the second syntax path, that is, with type-id and parentheses (Case 2)?

The only related quote is C++03:5.3.4/5:

When the allocated object is an array (that is, the direct-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array.

The wording (if any) would allow an array with no elements, however, it does not seem clear if it refers to both cases or only to the one with new-type-id and without parentheses (Case 1).

Thanks in advance.

Notes:

  1. ISO/IEC 14882:2003, Section 8.3.4, Paragraph 1:

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

  2. ISO/IEC 14882:2003, Section 5.3.4, Paragraph 6:

    The expression in a direct-new-declarator shall have integral or enumeration type (3.9.1) with a non-negative value.

  3. ISO/IEC 14882:2003, Section 5.3.4, Paragraph 7:

    When the value of the expression in a direct-new-declarator is zero, the allocation function is called to allocate an array with no elements.

  4. ISO/IEC 14882:2003, Section 5.3.4, Paragraph 1:

    new-expression:

    ::opt new new-placementopt new-type-id new-initializeropt

    ::opt new new-placementopt ( type-id ) new-initializeropt

  5. Although the above quotes are from the C++03 Standard, to the best of my knowledge this issue is still unclear in newer versions of the C++ Standard (C++11, C++14 and C++17).
  6. Interesting Herb Sutter's post about zero-length arrays.
  7. The code in the example is a slightly modified test from SolidSands' SuperTest suite.
like image 439
José Luis Avatar asked Apr 30 '18 18:04

José Luis


People also ask

What are dynamic arrays in C?

Dynamic arrays are resizable and provide random access for their elements. They can be initialized with variable size, and their size can be modified later in the program. Dynamic arrays are allocated on the heap, whereas VLAs are allocated on the stack.

Can you create an array of size 0 in C?

Zero-length array declarations are not allowed, even though some compilers offer them as extensions (typically as a pre-C99 implementation of flexible array members).

Do arrays have dynamic length?

One limitation of arrays is that they're fixed size, meaning you need to specify the number of elements your array will hold ahead of time. A dynamic array expands as you add more elements. So you don't need to determine the size ahead of time.

How do you find the length of a dynamic array?

You can't. The size of an array allocated with new[] is not stored in any way in which it can be accessed. Note that the return type of new [] is not an array - it is a pointer (pointing to the array's first element). So if you need to know a dynamic array's length, you have to store it separately.


1 Answers

With parenthesis you have a regular type-id, instead of the special syntax called a new-type-id that supports dynamic array size.

As of C++17 the standard makes no special provision for this use of a type-id, so the question boils down to whether you can write

auto main() -> int
{
    using Argh = int[0];
}

You can't, because a type-id's type is defined in terms of a fictitious “declaration for a variable or function of that type that omits the name of the entity” (C++17 §11.1/1), and for declaration of an array variable the rule is “If the constant-expression (8.20) is present, it shall be a converted constant expression of type std::size_t and its value shall be greater than zero” (C++17 §11.3.4/1).


Now there's a fair bit of interpretation in this. E.g. without such reasonable interpretation the last quote would not reasonably say that the array size must be non-negative and representable as a size_t. Instead, with no reasonable interpretation, it would literally say that a declaration such as

int x[42];

is invalid (of course it isn't), and would have to be expressed as

int x[std::size_t(42)];

Determining what is a reasonable interpretation or not used to be easy. One could just ask, does it make sense? So for the case above the answer would be no, and one could ditch that possibility.

However, to some degree with C++14 and increasingly more so with C++17 I find that that earlier dependable technique fails. Since the question at hand is about C++03 functionality, however, I think you can trust this answer. But if it were a question about C++14 or later stuff, then do keep in mind that any apparently clear-cut answer probably involves some subjective interpretation that probably can't be resolved by asking whether it makes sense or not.

like image 67
Cheers and hth. - Alf Avatar answered Oct 16 '22 11:10

Cheers and hth. - Alf