2

As we all know, strings in C are null-terminated. Does that mean that according to the standard it is legal to use the NULL constant as the terminator? Or is the similarity of the name of NULL pointer and null-terminator for a string only a happy coincidence?

Consider the code:

char str1[] = "abc";
char str2[] = "abc";
str1[3] = NULL;
str2[3] = '\0';

Here, we change the terminator of str1 to NULL. Is this legal and well-formed C code and str1 adheres to C's definition of null-terminated string? Will it be the same in case of C++?

In practice, I have always used NULL instead of '\0' in my code for strings and everything worked - but is such practice 100% legal?

EDIT: I understand that it's very bad style and refrain from endorsing it and now understand the difference between 0, NULL and '\0' (as in a duplicate What is the difference between NULL, '\0' and 0). I'm still quite curious as for the legality of this code - and voices here seem to be mixed - and the duplicate does not give an authoritative answer to that in my opinion.

lukeg
  • 4,189
  • 3
  • 19
  • 40
  • Note that [`NULL`](https://stackoverflow.com/q/924664/2602718) is deprecated in C++, but that it's essentially just `#define NULL 0`, so since `'\0'` is ASCII 0, this will work. – scohe001 Sep 21 '18 at 21:33
  • NULL as been said is deprecated and can cause unexpected behaviour and nullptr is replaced for pointers, '\0' is the convention for null-terminated strings – LiorA Sep 21 '18 at 21:35
  • @scohe001 But does C++ standard guarantee to use ASCII encoding so that always NULL == '\0'? – lukeg Sep 21 '18 at 21:35
  • @lukeg I would assume so, since `while(*my_cstr_ptr)` is common code to check if the pointer is not at the end of the string yet, but I'll leave it to others to quote the standard. – scohe001 Sep 21 '18 at 21:39
  • 6
    @lukeg 1) NULL has nothing to do with characters. NULL is for pointers: it means null pointer, even if it's just a `#define` and it's defined as 0, it suggests more than just a zero. 2) ASCII has nothing to do with `'\0'` which is just a `char` literal with value 0. It doesn't matter how characters are encoded, zero is zero. – curiousguy Sep 21 '18 at 21:41
  • @scohe001 One of the answers in the duplicate thread you suggested (https://stackoverflow.com/a/1296959/589916) seems to say otherwise: "In particular, do not use NULL when the ASCII null character (NUL) is desired." Therefore I would be grateful for an answer supported by the standardese. – lukeg Sep 21 '18 at 21:43
  • 4
    "_definition of NULL-terminated string_" There is no such thing as "NULL-terminated", capitalization strongly suggesting the use of `NULL` (the preprocessor macro): no C or C++ standard uses that spelling "NULL-terminated". There is no "NULL", it's **zero** terminated. (Or NUL terminated in term of ASCII.) – curiousguy Sep 21 '18 at 21:48
  • 4
    NULL is used for pointers so using it to terminate a string is odd. – Killzone Kid Sep 21 '18 at 21:48
  • @scohe001 "_Note that NULL is deprecated in C++_" when was it deprecated? – curiousguy Sep 21 '18 at 21:49
  • @Neil Butterworth Also I don't quite get why C++ tag was removed from my questions if it explicitly mentions C++ in its content. – lukeg Sep 21 '18 at 21:50
  • @lukeg Ask one question at a time. –  Sep 21 '18 at 21:56
  • 1
    @curiousguy Misread your comment, changed the capitalization in my question in the one place where I overlooked it. – lukeg Sep 21 '18 at 22:02
  • C and C++ are completely different languages. –  Sep 21 '18 at 22:24
  • 1
    @NeilButterworth C++ is based on C, has C declaration syntax, still uses C strings in many places, and the ability to directly interface with C is one of the most essential C++ selling point. The C/C++ subset is important and the question is about a part of that subset. – curiousguy Sep 21 '18 at 22:27
  • 1
    Eh. I'd flag it in a code review and ask for `'\0'` or some other obvious NUL constant like you have for the 2nd assignment. Reading that makes me think you are playing with pointers. Don't care if it's legal, it's confusing to the eye and therefore probably not a good idea. – Michael Dorgan Sep 21 '18 at 22:31
  • @curiousguy "has C declaration syntax" is not true. "has a declaration syntax similar in some ways to historic C versions" would be more accurate... but you can see where this is going. – aschepler Sep 21 '18 at 22:32
  • @curiousguy If you think it will add any value (which I really don't - if the OP is asking about C++, it should be a separate question) please feel free to re-tag. –  Sep 21 '18 at 22:32
  • @aschepler The declarator construct works the same way with a huge usable subset in C/C++. – curiousguy Sep 21 '18 at 22:39
  • 4
    all the debate over whether its correct per standard is interesting but irrelevant. 99% of code readers read NULL as a pointer, using where are char is intended will cause a pipeline stall in their brains, including the original author's, a few months later. Be obvious in yr code – pm100 Sep 21 '18 at 22:54

3 Answers3

3

Does that mean that according to the standard it is legal to use the NULL constant as the terminator? (OP)

str1[3] = NULL;

Sometimes. Further: does it always properly cause a character array to form a string without concerns?

First, it looks wrong. Akin to int z = 0.0;. Yes it is legal well defined code, but unnecessarily draws attention to itself.

In practice, I have always used NULL instead of '\0' (OP)

I doubt you will find any modern style guide or group of coders endorsing that. NULL is best reserved for pointer contexts.1

These are 2 common and well understood alternatives.

str1[3] = '\0';
str1[3] = 0;

strings in C are null-terminated (OP)

The C spec consistently uses null character, not just null.


The macros are NULL which expands to an implementation-defined null pointer constant; and ... C11 §7.19 3

OK, now what is a null pointer constant?

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. §6.3.2.3 5

If the null pointer constant is a void* then we have something like

str1[3] = (void*) 0;

The above can warn about converting a pointer to a char. This is something best avoided.


Will it be the same in case of C++? (OP)

Yes, the above applies. (Aside: str1[3] = 0 may warn.) Further, NULL is less preferred than nullptr. So NULL is rarely the best to use in C++ in any context.


1Note: @Joshua reports a style that matches OP's in 1995 Turbo C 4.5

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
3

The bottom line is that in C/C++, NULL is for pointers and is not the same as the null character, despite the fact that both are defined as zero. You might use NULL as the null character and get away with it depending on the context and platform, but to be correct, use '\0'. This is described in both standards:

  • C specifies that the macro NULL is defined as a macro in <stddef.h> which "expands to an implementation-defined null pointer constant" (Section 7.17.3), which is itself defined as "an integer constant expression with the value 0, or such an expression cast to type void *" (Section 6.3.2.3.3).

    The null character is defined in section 5.2.1.2: "A byte with all bits set to 0, called the null character, shall exist in the basic execution character set; it is used to terminate a character string." That same section explains that \0 will be the representation of this null character.

  • C++ makes the same distinctions. From section 4.10.1 of the C++ standard: "A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t." In section 2.3.3, it describes the as "null character (respectively, null wide character), whose value is 0". Section C.5.2 further confirms that C++ respects NULL as a standard macro imported from the C Standard Library.

Derek T. Jones
  • 1,800
  • 10
  • 18
1

No, I don't think it's strictly legal.

NULL is specified to be either:

  • an integer constant expression with the value ​0​
  • an integer constant expression with the value 0 cast to the type void*

In an implementation that uses the first format, using it as the string terminator will work.

But in an implementation that uses the second format, it's not guaranteed to work. You're converting a pointer type to an integer type, and the result of this is implementation-dependent. It happens to do what you want in common implementations, but nothing requires it.

If you have the second type of implementation, you're likely to get a warning like:

warning: incompatible pointer to integer conversion assigning to 'char' from 'void *' [-Wint-conversion]

If you want to use a macro, you can define:

#define NUL '\0'

and then use NUL instead of NULL. This matches the official name of the ASCII null character.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • To be clear, you are saying that `char c = (void*) 0;` is not defined in C? –  Sep 21 '18 at 22:23
  • 1
    Yes, that's what I'm saying. Am I wrong? – Barmar Sep 21 '18 at 22:24
  • Ahh, I see there's an exception to the implementation-dependency, it's well-defined for null pointers. – Barmar Sep 21 '18 at 22:25
  • As far as I can see, it's implementation-defined behavior, per https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p6 . (But I can't figure out what would be "previously specified" related to that sentence.) – aschepler Sep 21 '18 at 22:26
  • Hmm, https://wiki.sei.cmu.edu/confluence/display/c/INT36-C.+Converting+a+pointer+to+integer+or+integer+to+pointer claims **INT36-C-EX1: A null pointer can be converted to an integer; it takes on the value 0.** – Barmar Sep 21 '18 at 22:31
  • 2
    The initialization `char c = (void*) 0;` also violates 6.7.9/11 "the same constraints and conversions as for simple assignment apply" [to an initialization] and the constraint on valid type combinations listed in 6.5.16.1/1 for simple assignment. – aschepler Sep 21 '18 at 22:43
  • @aschepler I've reverted my answer to be consistent with those quotes from the standard. I don't know where the CMU page got its information (unless it's a change in a newer version of the standard). – Barmar Sep 21 '18 at 22:47
  • 2
    Surely `char c = (void*) 0;` will not compile. – Paul Sanders Sep 21 '18 at 22:54
  • 1
    @PaulSanders I get a warning, but it compiles: **warning: incompatible pointer to integer conversion initializing 'char' with an expression of type 'void *' [-Wint-conversion]** – Barmar Sep 21 '18 at 22:57
  • 1
    @Barmar OK, thanks. I'm a little surprised by that, I have to say, I thought those days were long gone. – Paul Sanders Sep 21 '18 at 22:59
  • @PaulSanders: It's a wonder what all a C- or C++-compiler tries to crowbar into "working" somehow, after issuing the requisite diagnostic, if just given leave to try. Use the proper incantation, and it won't be quite that insanely lenient. – Deduplicator Sep 21 '18 at 23:49
  • @Deduplicator Remember C's history as a "portable assembler". Lots of things that aren't officially specified, like type punning, generally does the intuitively expected thing. – Barmar Sep 21 '18 at 23:51