42

I was recently faced with a line of code and four options:

char fullName[30] = {NULL};
  1. A) First element is assigned a NULL character.

  2. B) Every element of the array is assigned 0 ( Zeroes )

  3. C) Every element of the array is assigned NULL

  4. D) The array is empty.

The answer we selected was option C, as, while the array is only initialized with a single NULL, C++ populates the rest of the array with NULL.

However, our professor disagreed, stating that the answer is A, he said:

So the very first element is NULL, and when you display it, it's displaying the first element, which is NULL.

The quote shows the question in its entirety; there was no other information provided. I'm curious to which one is correct, and if someone could explain why said answer would be correct.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jay B
  • 429
  • 4
  • 3
  • 9
    Options `A` and `B` aren't mutually exclusive. Formally, they are *both* true. – Adrian Mole May 10 '20 at 21:06
  • [Related-ish](https://stackoverflow.com/q/14797810/4386278) – Asteroids With Wings May 10 '20 at 21:49
  • I think it might depend on which compiler you are using. I do remember using this kind of initialisation in the early 90s and I'm fairly sure it just set the first char to \0. These were the days when NULL was defined as '\0', or (void)0, or other things depending on the compiler. – Neil May 12 '20 at 12:31
  • @Neil It shouldn't work with (void)0 because you cannot initialise something with a void expression. Unless that is something else different in ancient C++. '\0' would work. I've never seen NULL defined like that, although that doesn't mean that no one ever has defined it so. I have seen NULL used to initialise a character before, but then again, I've also seen memory leaks and other bugs before as well, so seeing something done doesn't necessarily mean that doing so has ever been correct. – eerorika May 12 '20 at 13:58
  • The sad truth is that the professor is always right. The _true_ truth is that he doesn't know an awful lot about C++. Although using `NULL` to initalize a `char` and confusing wording `NUL` with `NULL` is kinda disqualifying by itself, under the reasonable assumption that the macro evaluates to `0` (otherwise the build breaks anyway), answer B is very obviously the correct one. The standard explicitly says so, and it's very common knowledge, there's no room for discussion. But go ahead and tell your professor that, and you'll fail every assignment going forward... – Damon May 12 '20 at 15:08
  • Hmm, I would think that array elements are assigned elements listed on the RHS until they are exhausted, with the remaining members being unassigned. So if the RHS was {'a', 'b', 'c'}, the first three elements would be assigned and the remaining 27 would be unassigned. – MPW May 12 '20 at 15:12
  • 1
    @MPW You would think wrong. No element is left "unassigned". – eerorika May 12 '20 at 19:44
  • @eerorika : I mean allocated, but not initialized – MPW May 12 '20 at 19:55
  • 1
    @MPW All elements are initialised. – eerorika May 12 '20 at 19:56
  • @eerorika : Taking time to look it up, I now see you are right. I stand corrected. +1 for you (twice!). – MPW May 12 '20 at 20:00
  • In my opinion, _all_ answers are correct. The answers make you think that `\0` and `0` are different things, they are the same. Not so sure about the last one though, because as @eerorika pointed out, all elements are initialized, in this case, with zeros ( Which isn't the same thing as **empty** ). – Yazan Alsalem Aug 11 '20 at 15:16

4 Answers4

36

The question is ill-defined, but Option B seems like the most correct answer.

The result depends on how exactly NULL is defined, which depends on the compiler (more precisely, on the standard library implementation). If it's defined as nullptr, the code will not compile. (I don't think any major implementation does that, but still.)

Assuming NULL is not defined as nullptr, then it must be defined as an integer literal with value 0 (which is 0, or 0L, or something similar), which makes your code equivalent to char fullName[30] = {0};.

This fills the array with zeroes, so Option B is the right answer.

In general, when you initialize an array with a brace-enclosed list, every element is initialized with something. If you provide fewer initializers than the number of elements, the remaining elements are zeroed.

Regarding the remaining options:

  • Option C is unclear, because if the code compiles, then NULL is equivalent to 0, so option C can be considered equivalent to Option B.

  • Option A can be valid depending on how you interpret it. If it means than the remaining elements are uninitialized, then it's wrong. If it doesn't specify what happens to the remaining elements, then it's a valid answer.

  • Option D is outright wrong, because arrays can't be "empty".

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Option A is invalid because there’s no such thing as a “NULL character”. As noted in another comment, I feel that D could be argued to be correct (though imprecise) since the array represents an empty string. – Konrad Rudolph May 11 '20 at 10:52
  • 4
    @KonradRudolph I suppose by "NULL character" the author meant "NUL character", which `'\0'` is when interpreted as ASCII. – Ruslan May 11 '20 at 11:01
  • 2
    @Ruslan Yeah but the professor clearly isn’t aware, and equally clearly confuses NUL and NULL. – Konrad Rudolph May 11 '20 at 11:09
  • @KonradRudolph It could also mean, very literally, "a character with the same value as `NULL` macro". – HolyBlackCat May 11 '20 at 11:09
  • 2
    @HolyBlackCat Sure, but that terminology is unambiguously *incorrect*, whereas calling an array that holds an empty string itself “empty” is at worst imprecise. Anyway, this is all just pointless pedantry on my part. We can agree that the professor is wide off the mark. – Konrad Rudolph May 11 '20 at 12:51
  • 1
    Isn't there also the possibility of `NULL` being defined as `(void*)0` in older versions of C++, or was that always just C? – mtraceur May 11 '20 at 20:20
  • 3
    Minor pedantry: isn't `NULL` a *library* definition rather than a *compiler* one? We often use "defined by the compiler" as metonymy for "defined by the implementation" but if we want to be precise it's probably more correct to say "depends on the standard library implementation"? – mtraceur May 11 '20 at 20:20
  • 2
    @mtraceur -- `(void*)0` is not a valid null pointer constant in C++. Never has been. Unlike C, there is no implicit conversion from `void*` to other pointer types. So `int *ip = (void*)0;` is an error. – Pete Becker May 12 '20 at 13:16
  • I suspect the question was originally written for a C class, and the professor didn't realize that it's invalid in C++. – Barmar May 12 '20 at 19:27
  • @Barmar Seems to be equally invalid in C, if `NULL` ends up defined as `(void *)0`. – HolyBlackCat May 12 '20 at 19:44
  • @HolyBlackCat Invalid in standard C indeed. Perhaps it was originally written for a C class over 40 years ago? :) – eerorika May 12 '20 at 19:48
21
char fullName[30] = {NULL};

This is something that should never be written.

NULL is a macro that expands to a null pointer constant. A character - not a pointer - is being initialised here, so it makes no sense to use NULL.

It just so happens that some null pointer constants are also integer literals with value 0 (i.e. 0 or 0L for example), and if NULL expands to such literal, then the shown program is technically well-formed despite the abuse of NULL. What the macro expands to exactly is defined by the language implementation.

If NULLinstead expands to a null pointer constant that is not an integer literal such as nullptr - which is entirely possible - then the program is ill-formed.

NULL shouldn't be written in C++ at all, even to initialise pointers. It exists for backwards compatibility with C to make it easier to port C programs to C++.


Now, let us assume that NULL happens to expand to an integer literal on this particular implementation of C++.

Nothing in the example is assigned. Assignment is something that is done to pre-existing object. Here, and array is being initialised.

The first element of the array is initialised with the zero literal. The rest of the elements are value initialised. Both result in the null character. As such, the entire array will be filled with null characters.

A simple and correct way to write the same is:

char fullName[30] = {};

B and C are equally close to being correct, except for wording regarding "assignment". They fail to mention value initialisation, but at least the outcome is the same. A is not wrong either, although it is not as complete because it fails to describe how the rest of the elements are initialised.

If "empty" is interpreted as "contains no elements", then D is incorrect because the array contains 30 elements. If it is interpreted as "contains the empty string", then D would be a correct answer.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 2
    I’d even accept answer D as correct: “empty” isn’t a precisely defined term, but for a reasonable definition of the term, the array is indeed empty, since it semantically represents an empty C-style string. The *only* answer that is unambiguously wrong is A. – Konrad Rudolph May 11 '20 at 10:51
  • @KonradRudolph Personally, I understand "empty" as "contains no elements". An array with 30 elements would therefore not be empty. I guess there can be other interpretations then. – eerorika May 11 '20 at 10:56
  • I mean, your interpretation clearly isn’t unreasonable, and was probably intended here. My point was mostly that, ironically, the professor’s preferred answer is the *least* correct. – Konrad Rudolph May 11 '20 at 10:58
  • @KonradRudolph With very small, and possibly subtle change to wording of the question, D would be unambiguously correct though: The array does contain the empty null terminated string. – eerorika May 11 '20 at 11:00
  • 1
    +1. About "empty", for a fixed-sized array, I understand it to mean "containing undefined values". For a dynamic array, it would refer to having a length of zero, but this is not a dynamic array. You can see other examples of this usage in the wild e.g. with [`numpy.empty(...)`](https://numpy.org/doc/stable/reference/generated/numpy.empty.html). – geometrian May 12 '20 at 00:03
  • Regarding the suggestion to use `{}` -- I presume that the point of the question is to address how C++ treats an initialization list that only initializes part of an array -- what happens to the rest of the elements? – Barmar May 12 '20 at 19:31
  • @Barmar As the answer states: *"The rest of the elements are value initialised"*, and this results in the null character for the elements. Although the result is same, using value initialisation for all elements is simpler than using explicit initialisation for one element and value initialisation for rest of the elements, which is why I recommend the former. – eerorika May 12 '20 at 19:40
  • @Barmar Regarding presumed point of the multi-choice question by the professor, I'm not even sure what point they are after given the multitude of subtle mistakes. But if the point is what happens to the rest of the elements, I would recommend them to change the example to initialise the first element with expression of correct type, and preferably some other value than zero, and then fix all of the options so that there is single correct answer (assuming single choice). – eerorika May 12 '20 at 20:01
  • @eerorika As I mentioned in another comment, it looks like this question was taken from a C course without realizing that it was invalid in C++. – Barmar May 12 '20 at 20:07
  • @Barmar But it's equally invalid in C as well: https://godbolt.org/z/CcFKvt And the answers are equally misleading in C. The correct answer is still that the array is full of null characters whose value are zero. – eerorika May 12 '20 at 20:18
10

You are almost correct.

The professor is incorrect. It is true that display finishes at the first NULL (when some approaches are used), but that says nothing about the values of the remainder of the array, which could be trivially examined regardless.

[dcl.init/17.5]:: [..] the ith array element is copy-initialized with xi for each 1 ≤ i ≤ k, and value-initialized for each k < i ≤ n. [..]

However, none of the options is strictly correct and well-worded.

What happens is that NULL is used to initialise the first element, and the other elements are zero-initialised. The end result is effectively Option B.

Thing is, if NULL were defined as an expression of type std::nullptr_t on your platform (which it isn't, but it is permitted to be), the example won't even compile!

NULL is a pointer, not a number. Historically it has been possible to mix and match the two things to some degree, but C++ has tried to tighten that up in recent years, and you should avoid blurring the line.

A better approach is:

char fullName[30] = {};

And the best approach is:

std::string fullName;
Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
0

Apparently, Your Professor is right, let's see how

char someName[6] = "SAAD";

how the string name is represented in memory:

0 1 2 3 4 5

S A A D

Array-based C string

The individual characters that make up the string are stored in the elements of the array. The string is terminated by a null character. Array elements after the null character are not part of the string, and their contents are irrelevant.

A "null string" is a string with a null character as its first character:

0 1 2 3 4 5

/0

Null C string

The length of a null string is 0.

Community
  • 1
  • 1