Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with aggregate initialization with the new operator

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?

like image 972
petat_irrumator Avatar asked Apr 07 '26 20:04

petat_irrumator


2 Answers

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*
like image 72
Jan Schultke Avatar answered Apr 09 '26 11:04

Jan Schultke


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};
like image 39
Amir Kirsh Avatar answered Apr 09 '26 10:04

Amir Kirsh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!