19

If I write

int zero = 0;

void *p1 = (void *)0;
void *p2 = (void *)(int)0;
void *p3 = (void *)(0 /*no-op, but does it affect the next zero?*/, 0);

void *p4 = (void *)zero;    // For reference, this is a pointer to address zero
void *p5 = 0;               // For reference, this is a null pointer
void *p6 = NULL;            // For reference, this is a null pointer
void *p7 = nullptr;         // For reference, this is a null pointer (C++11)

static const int static_zero_1 = 0;       // Is this a literal zero when used?
static const int static_zero_2 = 1 - 1;   // No "literals 0" per se... is it?
void *p8 = (void *)static_zero_1;   // I have seen weird substitution rules...
void *p9 = (void *)static_zero_2;   // do they apply for NULL too?

which of p1, p2, and p3 (edit: I added p8 and p9) would be null pointers (i.e. == NULL, may or may not be address zero), and which of them would be pointers with the address zero (may or may not be == NULL)?

If the answer is different in C and C++, what is it in each of them?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • Isn't a null pointer defined by "it points to address 0"? – leemes May 15 '13 at 10:52
  • p4 is also nullptr. Possibly you meant void *p4 = &zero; – Andrew May 15 '13 at 10:52
  • The integer literal value `0` is, implicitly or explicitly converted to `void *`, is considered a `NULL` pointer. So p1, p2, p3, p5, p6 and p7 are all `NULL`. –  May 15 '13 at 10:54
  • 3
    @leemes: No, see [this answer](http://stackoverflow.com/a/2759875/541686). – user541686 May 15 '13 at 10:54
  • 1
    @Andrew: I meant to write what I wrote -- are you sure that's also nullptr? My understanding was that it's not, because it's not using a literal 0. – user541686 May 15 '13 at 10:55
  • 1
    @Andrew Don't try to second guess Mehrdad - he's smarter than that. –  May 15 '13 at 10:55
  • @H2CO3: lol thanks. Could you explain why `p2` and `p3` are both null? Is `(int)0` considered a "literal" zero, so that it becomes a null pointer? And same with `(0, 0)`? – user541686 May 15 '13 at 10:57
  • So according to your link, I'd guess `p1`, `p5`, `p6`, `p7` are null pointers, while `p2`, `p3`, `p4` are pointers with address 0 (because they are *expressions* but not *literal constants* with a value of 0, if I understand the linked answer correctly) – leemes May 15 '13 at 10:58
  • @Mehrdad *As far as I know,* `(void *)(int)0` is the literal `0`, explicitly converted to `int` then to `void *`. What about `(0, 0)`? Well, as I am starring at it for a longer time, it may not actually be the literal. It's an expression which evaluates to 0. I have to think twice before deciding which one it is... –  May 15 '13 at 10:59
  • @leemes: That was my guess too, but I wasn't sure -- in fact, let me throw in another example just to make it clearer why it's confusing me what's considered a constant and what's considered an expression! – user541686 May 15 '13 at 11:00
  • BTW, how come you got no upvotes so far? Take my one. –  May 15 '13 at 11:00
  • @H2CO3: Lol thanks again. Yeah it's confusing to me, because I don't understand what's considered a literal and what's a (constant?) expression in C, if that differs in any way from C++, and how the language treats those in terms of being null. I through in a couple more examples to show what I mean -- it's really confusing me how the language treats each of them. – user541686 May 15 '13 at 11:02
  • @Mehrdad In C, AFAIK it must be a literal, whereas in C++, apparently a [constant expression is enough](http://stackoverflow.com/questions/7016861/null-pointer-in-c-and-c), but I might be wrong regarding the first part. –  May 15 '13 at 11:04
  • @H2CO3: Interesting... I'm not sure how that would affect the static const examples, but that would mean `p2` or `p3` might be different in C and C++? – user541686 May 15 '13 at 11:06
  • 1
    @Mehrdad No, I meant that `0` and `(0, 0)` would differ since the latter is not a literal. Fortunately, I was wrong and it is not the case - see my answer. –  May 15 '13 at 11:09

3 Answers3

9

And to make Andy's answer complete with C:

From the C99 Standard:

6.3.2.3 Pointers

1 A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 55) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

So any integer constant expression which evaluates to 0 is a null pointer constant and can be converted to a NULL pointer. Effectively in your example, all pointers through except p4, p8 and p9 are null pointers. p4, p8 and p9 need not be null pointers since their initialization is not a constant expression because it contains variables (even if const qualified).

Here's another answer about NULL in C++, for the record.

Community
  • 1
  • 1
  • +1 *An integer constant expression with the value `0`, or such an expression cast to type `void *`*... ah, I guess that means they're *all* going to be null pointers in C (aside from `p4`), huh? – user541686 May 15 '13 at 11:09
  • Now the hilarious thing is, if I said `const int zero = 0`, `p4` would still be address zero. But if I said `static const int zero = 0`, then it'd become a null pointer, right?! o_O – user541686 May 15 '13 at 11:12
  • 1
    @Mehrdad Right, it's worth looking at section 6.6 of the Standard, I've just found it! Indeed, there's a reference to `static` in there (however, that's within the context of the addressof operator, which is slightly different). –  May 15 '13 at 11:14
  • In C, `p8` and `p9` aren't forced to be null pointers either. This comes from the fact that the term "integer constant expression" has a very specific definition in C, that excludes e.g `const` qualified variables as they are used for the initialization of `p8` and `p9`. – Jens Gustedt May 15 '13 at 12:07
  • 1
    @Mehrdad No. The static makes no difference. – James Kanze May 15 '13 at 12:09
  • @JensGustedt I didn't notice the edit on the question, my answer holds for pointers `p1...p7`, I'll update it with the correction, thanks! –  May 15 '13 at 12:50
  • So does this mean that the `NULL` macro has to expand to 0? Since section 7.17 states that `NULL` expands to an "implementation-defined null pointer constant", 2 could be a perfectly valid null pointer. – rm5248 May 15 '13 at 15:14
  • @rm5248 Yes, `p2` is a null pointer, and the `NULL` macro has to either expand to an integer constant expression evaluating to 0. I'm not sure if it's permitted by the standard to expand to an internal compiler magic keyword, for example `__null` (that's what the standard library on OS X does recently). –  May 15 '13 at 15:17
  • @JamesKanze: Ah... I thought `static` had to be there for it to be a constant expression, but evidently not, thanks! – user541686 May 15 '13 at 19:26
  • @Mehrdad It's the reverse. If this is at namespace scope, then `const` implies `static` as the default. – James Kanze May 16 '13 at 08:05
8

which of p1, p2, and p3 would be null pointers?

In C++11, all of them. Per paragraph 4.10/1 of the C++11 Standard:

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. [...]

Therefore, according to the terminology of the Standard, everything which is a constant (integral) expression and evaluates to 0 is a null pointer constant (not a null pointer, yet). The only one which is not a constant expression that evaluates to 0 or a prvalue of type nullptr_t in your example is zero, because it is not a constant expression.

The paragraph continues:

A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal.

So in your example all the pointers except p4 are null pointer values and compare equal among themselves.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Wow, so in C++11 it's really easy! +1 thanks, that answers at least part of the question. – user541686 May 15 '13 at 11:07
  • @Mehrdad: Right, I am not a C guy so I'll leave the other part to someone with more experience ;) – Andy Prowl May 15 '13 at 11:26
  • @AndyProwl I did it - and I left out the C++ part because I'm not a C++ guy :D –  May 15 '13 at 11:30
  • @H2CO3: Oh, right, of course - I failed to see that you were quoting the C99 Standard ;) Well done – Andy Prowl May 15 '13 at 11:33
  • `p3` is _not_ a null pointer constant, since it is not a constant expression. The wording in C++11 is more complex, but it is clear that constant expressions are "conditional-expression" meeting all of the other criteria. A "conditional-expression" cannot contain a comma operator (or any assignment operator). – James Kanze May 15 '13 at 12:12
  • And of course, in C, neither `p8` nor `p9` are null pointers. This is one place where C and C++ differ. – James Kanze May 15 '13 at 12:13
  • @AndyProwl I don't know. Probably a bug in the compiler. The standard is quite clear: "constant-expresssion: conditional-expression", so no comma operators or assignment operators are allowed. – James Kanze May 15 '13 at 12:18
  • @AndyProwl And g++ complains about `int arr[ (0, 42) ];`, at least when invoked with `-std=c++11 -pedantic`. It's just a warning, but a warning is a diagnostic in the sense of the standard (according to the g++ documentation). – James Kanze May 15 '13 at 12:20
  • @AndyProwl MSVC rejects `int arr[ (0, 42) ]'` as well (with `-Za`, at least). With an error, not just a warning. – James Kanze May 15 '13 at 12:22
  • @JamesKanze: I don't understand why a *conditional-expression* could not contain the comma operator, could you please explain? – Andy Prowl May 15 '13 at 12:30
  • @JamesKanze: If I follow the grammar, *conditional-expression* can be a *logical-or-expression*, which can be a *logical-and-expression*, which can be a *inclusive-or-expression*, which [...] can be a *unary-expression*, which can be a *postfix-expression*, which can be a *primary-expression*, which can be an *expression*, which can be an *expression , assignment-expression*. And while § 5.19 does specifically forbid assignments, it says nothing about the conditional operator. – Andy Prowl May 15 '13 at 12:36
  • @AndyProwl A primary-expression cannot be an expression. – James Kanze May 15 '13 at 15:08
  • @JamesKanze: According to A.4 a `primary-expression` can be an `( expression )` – Andy Prowl May 15 '13 at 15:10
  • @AndyProwl Yes. A `primary-expression` can contain an `expression`. But looking further, it may be that this something that was added to C++11 (it is expressedly forbidden in C++03 and C); if so, none of the compilers I have access to (g++ 4.7.2 and MSVC 11) support it yet. – James Kanze May 15 '13 at 15:17
  • @JamesKanze: The link I posted in a previous comment is using g++4.7.2 with the `std=c++11` flag – Andy Prowl May 15 '13 at 15:18
  • The code you posted in a previous comment causes a diagnostic with `g++ -std=c++11 -pedantic`. In other words, g++ considers it contrary to the standard, but supports it as an extension. Other compilers, like MSVC, reject it out of hand. – James Kanze May 15 '13 at 15:21
  • @JamesKanze: What does the diagnostic say? I am using it here on my PC and I do not see any diagnostic apart from "unused variable" – Andy Prowl May 15 '13 at 15:25
  • @AndyProwl Yes. The warning is on a different line. Since I was looking for it (and already had an error from MSVC), I just reacted when I saw diagnostic output, without reading it too carefully. And there's no diagnostic for `-std=c++03`, either, where it is clearly forbidden. (C++03 simply copies the definition from C.) – James Kanze May 15 '13 at 16:05
8

p1 and p2 are null pointers; p3 is implementation defined, and may be something else. (A comma operator cannot be part of a constant expression. And the mapping of a non-constant integral value 0 to a pointer is implementation defined.) C is identical to C++ here.

p8 and p9 are both null pointers in C++, but not in C.

With regards to your comment on static_zero_2, there is no requirement in either language that a literal zero be present, anywhere. g++ defines NULL as the compiler built-in __null, for example, and you can use (1 - 1), or '\0', or any other constant expression evaluating to 0.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I don't understand why `p3` is implementation defined. Where does the Standard say that a comma operator cannot be part of a constant expression? – Andy Prowl May 15 '13 at 12:41
  • 1
    @AndyProwl in 6.6 "constant expressions", p3 "*Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, ...*" – Jens Gustedt May 15 '13 at 13:03
  • @JensGustedt: That's C, I'm talking about C++11 – Andy Prowl May 15 '13 at 13:03
  • @AndyProwl, ah, you didn't say that, in C++11 you'd have to look for yourself, but chances are that this is similar. – Jens Gustedt May 15 '13 at 13:05
  • But seeing that para 6.6 p 3 it is in the "constraints" section so actually this is not implementation defined. In C at least, this isn't a constant expression. – Jens Gustedt May 15 '13 at 13:06
  • @JensGustedt: Sorry for not specifying that, in fact the question was implicitly for James, who wrote something along the same lines as a comment to my answer - and my answer deals explicitly with C++11. Moreover, the first sentence of this answer seems to target both C and C++ – Andy Prowl May 15 '13 at 13:09
  • @AndyProwl The fact remains that the expression must be a conditional-expression, which doesn't allow the comma operator. And that it doesn't work with any of the compilers I have access to, at least not in C++11 conformant mode. _If_ this is something that was added to C++11, neither g++ nor MSVC support it yet. – James Kanze May 15 '13 at 15:13
  • @JamesKanze: In the comment to my answer I linked a live example proving the contrary. And I still don't see why `The fact remains that the expression must be a conditional-expression` implies that a comma operator cannot be used. That might be true in C, but it's not obvious at all in C++11 (see comments to my answer) – Andy Prowl May 15 '13 at 15:15
  • @AndyProwl I think you've convinced me with regards to C++11. It's a newly added feature, however, which, as far as I can see, most compilers don't support yet. – James Kanze May 15 '13 at 15:18
  • @AndyProwl Question: is it a new feature in C++11, or is it simply something that got overlooked when the text was reworded? – James Kanze May 15 '13 at 15:22
  • @JamesKanze: Unfortunately I am not experienced enough to tell. For me, C++ is mostly C++11. I am admittedly ignorant about C, and I've never read the C++03 Standard. However, when I read the C++11 specification, I can't figure out where the comma operator would be forbidden explicitly or implicitly – Andy Prowl May 15 '13 at 15:28
  • @Andy Prowl - sounds like asking about the comma operator in that context is worth doing... – jim mcnamara May 15 '13 at 15:38
  • @AndyProwl It's hard to say. As long as I've known C++ (and C before that), the comma operator has been forbidden in a constant expression. On the other hand, I can't really see what problems it would create (unlike e.g. assignment). – James Kanze May 15 '13 at 15:58
  • @JamesKanze: I will try to do some research and if I will fail to figure it out I will eventually ask a specific question on SO – Andy Prowl May 15 '13 at 16:01
  • @AndyProwl The best place to ask would be in the newsgroups (comp.std.c++). But I've not seen any activity there for awhile. (All of the moderators are probably at the standards meeting in Bristol at the moment.) – James Kanze May 15 '13 at 16:07
  • +1 accepting this one because it pointed out the comma issue. All the answers were great though. – user541686 May 15 '13 at 19:31