11

The following two codes behave as expected:

char* test1 = new char[20]{"abc"};
cout << test1;

Outputs: abc

int size = 20;
char* test1 = new char[size]{'a','b','c'};
cout << test1;

Outputs: abc

But, the following code behaves unexpectedly in gcc and MSVC, while not in clang:

int size = 20;
char* test1 = new char[size]{"abc"};
cout << test1;

Outputs: random values

Why does it not output abc like the other examples?

6
  • 2
    What compiler version? For example if I go back a few years I see stuff like godbolt.org/z/KPb9f4zzo . This gives a strong suspicion that the address of the literal is being stored in the array rather than the data. Commented Jun 4 at 20:29
  • 1
    I can find rules for initializing a char array with a string literal with an array with no specified size, which you can't do with a dynamically allocated array, and with a specified constant size. I can't find anything in the Standard, particularly dcl-init that covers a dynamic size. Doesn't mean I haven't missed it, but you may have found a gray area. Commented Jun 4 at 21:35
  • 1
    Can see some interesting behaviour, though: godbolt.org/z/sTb33WMM5 . it looks like it zeroes the correct regions, but doesn't copy the string literal in. Commented Jun 4 at 21:48
  • 1
    I suspect that the decltype of new char[20] is int(*)[20] and the decltype of new char[size] is int*, and this is a reason of the distinct behaviors. Commented Jun 4 at 22:28
  • 3
    cppreference says new char[N]{...} performs aggregate initialization, and aggregate initialization of a char[] can be from a string literal. So, new char[size]{"abc"} should be treated as new char[size]{'a', 'b', 'c'} and so gcc and msvc would be wrong if they don't handle that correctly. Commented Jun 5 at 0:26

2 Answers 2

7

This is clearly a GCC and MSVC bug, although I could not find an existing bug report for it. Clang seems to handle it properly.

Note that there exists wording to cover this case in [expr.new] p8:

If the expression in a noptr-new-declarator is present, it is implicitly converted to std​::​size_t. The value of the expression is invalid if

  • [...]
  • the new-initializer is a braced-init-list and the number of array elements for which initializers are provided (including the terminating '\0' in a string-literal ([lex.string])) exceeds the number of elements to initialize.

What you are doing is expected and valid; the expression size is present, and "abc" only provides an initializer for four elements (including the terminating '\0'), which does not exceed the 20 elements you're constructing.

The initialization follows the rules in [dcl.init.string], which covers both "abc" and {"abc"}. The remaining 16 elements of the array should be zero.

Workaround

You should consider using std::string or performing the initialization in two steps:

  1. new char[size]{}, leaving everything zero initially
  2. std::memcpy the string literal into the char*
Sign up to request clarification or add additional context in comments.

1 Comment

I tend to agree, and opened yesterday a bug on gcc. In case one argues that the size in the expression must be known at compile time to hold for [expr.new] p8, the paragraph continues with: (8.5) if the expression is a potentially-evaluated core constant expression, the program is ill-formed; (8.6) otherwise, an allocation function is not called; [...] -- so it seems as a bug in gcc and msvc (valid code, omitting the initialization).
2

As commented, I agree with the answer given by Jan Schultke.

Though this is a valid and good question pointing at a probable bug in compiler implementations, it is worth mentioning that the actual usage of this would be quite rare, for the following reasons:

There is no reason to allocate the array for a size that may be smaller than required. If the size is smaller than the string literal provided (including the terminating '\0') this would be a runtime error, which throws a runtime exception for both gcc and clang.

We can allocate the array to the exact required size, by simply not providing the size:

auto test1 = new char[]{"abc"}; // allocation of 4 chars

In case we want for some reason to calculate the size, it should better be calculated it at compile time, to get compilation error instead of runtime exception, for too small size. Here is an example:

#define STR "abc" // may change

// we don't support strings longer than 4 chars and don't want to trim
// compilation error if STR size is bigger than 4
constexpr size_t max_size = 5;
constexpr size_t size = std::min(sizeof(STR), max_size);
  // (std::min is constexpr since C++14)
auto test1 = new char[size]{STR};

Cases where the size would not be known at compile time would be quite rare, but can happen, for example:

#define STR "abc" // may change

size_t size = getSizeFromDB();
assert(size >= sizeof(STR));
auto test1 = new char[size]{STR};

Or:

#define STR "abc" // may change

size_t min_size = getMinSizeFromDB();
size_t size = std::max(sizeof(STR), min_size);
auto test1 = new char[size]{STR};

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.