Short answer:

In both C and C++, (int *)0 is a constant expression whose value is a null pointer. It is not, however, a null pointer constant. The only observable difference between a constant-expression-whose-value-is-a-null-pointer and a null-pointer-constant, that I know of, is that a null-pointer-constant can be assigned to an lvalue of any pointer type, but a constant-expression-whose-value-is-a-null-pointer has a specific pointer type and can only be assigned to an lvalue with a compatible type. In C, but not C++, (void *)0 is also a null pointer constant; this is a special case for void * consistent with the general C-but-not-C++ rule that void * is assignment compatible with any other pointer-to-object type.

For example:

long *a = 0;           // ok, 0 is a null pointer constant
long *b = (long *)0;   // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0;   // ok in C, invalid conversion in C++
long *d = (int *)0;    // invalid conversion in both C and C++

And here's a case where the difference between the null pointer constant (void *)0 and a constant-expression-whose-value-is-a-null-pointer with type void * is visible, even in C:

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp a = 0;                  // ok, null pointer constant
fp b = (void *)0;          // ok in C, invalid conversion in C++
fp c = (void *)(void *)0;  // invalid conversion in both C and C++

Also, it's moot nowadays, but since you brought it up: No matter what the bit representation of long *'s null pointer is, all of these assertions behave as indicated by the comments:

// 'x' is initialized to a null pointer
long *x = 0;

// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);

assert (x == 0);         // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0);  // invalid comparison in both C and C++

assert (memcmp(&x, &y, sizeof y) == 0); // unspecified

assert (y == 0);         // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x);         // UNDEFINED BEHAVIOR: y may be a trap representation

"Unspecified" comparisons do not provoke undefined behavior, but the standard doesn't say whether they evaluate true or false, and the implementation is not required to document which of the two it is, or even to pick one and stick to it. It would be perfectly valid for the above memcmp to alternate between returning 0 and 1 if you called it many times.


Long answer with standard quotes:

To understand what a null pointer constant is, you first have to understand what an integer constant expression is, and that's pretty hairy -- a complete understanding requires you to read sections 6.5 and 6.6 of C99 in detail. This is my summary:

  • A constant expression is any C expression which the compiler can evaluate to a constant without knowing the value of any object (const or otherwise; however, enum values are fair game), and which has no side effects. (This is a drastic simplification of roughly 25 pages of standardese and may not be exact.)

  • Integer constant expressions are a restricted subset of constant expressions, conveniently defined in a single paragraph, C99 6.6p6 and its footnote:

    An integer constant expression96 shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

    96 An integer constant expression is used to specify the size of a bit-field member of a structure, the value of an enumeration constant, the size of an array, or the value of a case constant. Further constraints that apply to the integer constant expressions used in [#if] are discussed in 6.10.1.

    For purpose of this discussion, the important bit is

    Cast operators ... shall only convert arithmetic types to integer types

    which means that (int *)0 is not an integer constant expression, although it is a constant expression.

The C++98 definition appears to be more or less equivalent, modulo C++ features and deviations from C. For instance, the stronger separation of character and boolean types from integer types in C++ means that the C++ standard speaks of "integral constant expressions" rather than "integer constant expressions", and then sometimes requires not just an integral constant expression, but an integral constant expression of integer type, excluding char, wchar_t, and bool (and maybe also signed char and unsigned char? it's not clear to me from the text).

Now, the C99 definition of null pointer constant is what this question is all about, so I'll repeat it: 6.3.2.3p3 says

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 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.

Standardese is very, very literal. Those two sentences mean exactly the same thing as:

An integer constant expression with the value 0 is called a null pointer constant.
An integer constant expression with the value 0, cast to type void *, is also a null pointer constant.
When any null pointer constant is converted to a pointer type, the resulting pointer is called a null pointer and is guaranteed to compare unequal ...

(Italics - definition of term. Boldface - my emphasis.) So what that means is, in C, (long *)0 and (long *)(void *)0 are two ways of writing exactly the same thing, namely the null pointer with type long *.

C++ is different. The equivalent text is C++98 4.10 [conv.ptr]:

A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.

That's all. "Integral constant expression rvalue of integer type" is very nearly the same thing as C99's "integer constant expression", but there are a few things that qualify in C but not C++: for instance, in C the character literal '\x00' is an integer constant expression, and therefore a null pointer constant, but in C++ it is not an integral constant expression of integer type, so it is not a null pointer constant either.

More to the point, though, C++ doesn't have the "or such an expression cast to void *" clause. That means that ((void *)0) is not a null pointer constant in C++. It is still a null pointer, but it is not assignment compatible with any other pointer type. This is consistent with C++'s generally pickier type system.


C++ 2011 and C 2023 both revised the concept of "null pointer", adding a special type for them (nullptr_t) and a new keyword which evaluates to a null pointer constant (nullptr). I do not fully understand the changes and am not going to try to explain them, but I am pretty sure that a bare 0 is still a valid null pointer constant in both.

Answer from zwol on Stack Overflow
🌐
Home Assistant
community.home-assistant.io › configuration
Int(0) vs int(default=0) - Configuration - Home Assistant Community
July 18, 2025 - Hi! I don’t find a proper information about this, so, I am asking the experts, which one is recommended to use in home assistant template and what is the difference between those two? I saw that both have the same resu…
Top answer
1 of 2
35

Short answer:

In both C and C++, (int *)0 is a constant expression whose value is a null pointer. It is not, however, a null pointer constant. The only observable difference between a constant-expression-whose-value-is-a-null-pointer and a null-pointer-constant, that I know of, is that a null-pointer-constant can be assigned to an lvalue of any pointer type, but a constant-expression-whose-value-is-a-null-pointer has a specific pointer type and can only be assigned to an lvalue with a compatible type. In C, but not C++, (void *)0 is also a null pointer constant; this is a special case for void * consistent with the general C-but-not-C++ rule that void * is assignment compatible with any other pointer-to-object type.

For example:

long *a = 0;           // ok, 0 is a null pointer constant
long *b = (long *)0;   // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0;   // ok in C, invalid conversion in C++
long *d = (int *)0;    // invalid conversion in both C and C++

And here's a case where the difference between the null pointer constant (void *)0 and a constant-expression-whose-value-is-a-null-pointer with type void * is visible, even in C:

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp a = 0;                  // ok, null pointer constant
fp b = (void *)0;          // ok in C, invalid conversion in C++
fp c = (void *)(void *)0;  // invalid conversion in both C and C++

Also, it's moot nowadays, but since you brought it up: No matter what the bit representation of long *'s null pointer is, all of these assertions behave as indicated by the comments:

// 'x' is initialized to a null pointer
long *x = 0;

// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);

assert (x == 0);         // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0);  // invalid comparison in both C and C++

assert (memcmp(&x, &y, sizeof y) == 0); // unspecified

assert (y == 0);         // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x);         // UNDEFINED BEHAVIOR: y may be a trap representation

"Unspecified" comparisons do not provoke undefined behavior, but the standard doesn't say whether they evaluate true or false, and the implementation is not required to document which of the two it is, or even to pick one and stick to it. It would be perfectly valid for the above memcmp to alternate between returning 0 and 1 if you called it many times.


Long answer with standard quotes:

To understand what a null pointer constant is, you first have to understand what an integer constant expression is, and that's pretty hairy -- a complete understanding requires you to read sections 6.5 and 6.6 of C99 in detail. This is my summary:

  • A constant expression is any C expression which the compiler can evaluate to a constant without knowing the value of any object (const or otherwise; however, enum values are fair game), and which has no side effects. (This is a drastic simplification of roughly 25 pages of standardese and may not be exact.)

  • Integer constant expressions are a restricted subset of constant expressions, conveniently defined in a single paragraph, C99 6.6p6 and its footnote:

    An integer constant expression96 shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

    96 An integer constant expression is used to specify the size of a bit-field member of a structure, the value of an enumeration constant, the size of an array, or the value of a case constant. Further constraints that apply to the integer constant expressions used in [#if] are discussed in 6.10.1.

    For purpose of this discussion, the important bit is

    Cast operators ... shall only convert arithmetic types to integer types

    which means that (int *)0 is not an integer constant expression, although it is a constant expression.

The C++98 definition appears to be more or less equivalent, modulo C++ features and deviations from C. For instance, the stronger separation of character and boolean types from integer types in C++ means that the C++ standard speaks of "integral constant expressions" rather than "integer constant expressions", and then sometimes requires not just an integral constant expression, but an integral constant expression of integer type, excluding char, wchar_t, and bool (and maybe also signed char and unsigned char? it's not clear to me from the text).

Now, the C99 definition of null pointer constant is what this question is all about, so I'll repeat it: 6.3.2.3p3 says

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 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.

Standardese is very, very literal. Those two sentences mean exactly the same thing as:

An integer constant expression with the value 0 is called a null pointer constant.
An integer constant expression with the value 0, cast to type void *, is also a null pointer constant.
When any null pointer constant is converted to a pointer type, the resulting pointer is called a null pointer and is guaranteed to compare unequal ...

(Italics - definition of term. Boldface - my emphasis.) So what that means is, in C, (long *)0 and (long *)(void *)0 are two ways of writing exactly the same thing, namely the null pointer with type long *.

C++ is different. The equivalent text is C++98 4.10 [conv.ptr]:

A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.

That's all. "Integral constant expression rvalue of integer type" is very nearly the same thing as C99's "integer constant expression", but there are a few things that qualify in C but not C++: for instance, in C the character literal '\x00' is an integer constant expression, and therefore a null pointer constant, but in C++ it is not an integral constant expression of integer type, so it is not a null pointer constant either.

More to the point, though, C++ doesn't have the "or such an expression cast to void *" clause. That means that ((void *)0) is not a null pointer constant in C++. It is still a null pointer, but it is not assignment compatible with any other pointer type. This is consistent with C++'s generally pickier type system.


C++ 2011 and C 2023 both revised the concept of "null pointer", adding a special type for them (nullptr_t) and a new keyword which evaluates to a null pointer constant (nullptr). I do not fully understand the changes and am not going to try to explain them, but I am pretty sure that a bare 0 is still a valid null pointer constant in both.

2 of 2
12

Evaluating the expression (int*)0 yields a null pointer of type int*.

(int*)0 is not a null pointer constant.

A null pointer constant is a particular kind of expression that may appear in C source code. A null pointer is a value that may occur in a running program.

C and C++ (being two distinct languages) have slightly different rules in this area. C++ doesn't have the "or such an expression cast to type void*" wording. But I don't think that affects the answer to your question.

As for your question about (long long)123, I'm not sure how it's related, but the expression 123 is of type int, and the cast specifies a conversion from int to long long.

I think the core confusion is an assumption that the cast in (int*)0 does not specify a conversion, since 0 is already a null pointer constant. But a null pointer constant is not necessarily an expression of pointer type. In particular, the expression 0 is both a null pointer constant and an expression of type int; it is not of any pointer type. The term null pointer constant needs to be thought of as a single concept, not a phrase whose meaning depends on the individual words that make it up.

🌐
Reddit
reddit.com › r/learnprogramming › what is the difference between int x{} and int x = 0? and what about int x{5} and int x = 5?
r/learnprogramming on Reddit: What is the difference between int x{} and int x = 0? And what about int x{5} and int x = 5?
May 27, 2019 -

I saw an answer for the first comparison in Stack Overflow. The top reply said that int x = 0 is an example of copy initialization, where in this case x is initialized with the value 0. In the case of int x{} , it is apparently an example of zero initialization. What exactly is zero initialization? Does it initialize x with 0 in a different way? As far as I know, in both methods both variables are assigned the value 0, right?

As for the second comparison, that's just to see what would be different if there was an integer value inside the {} in int x{} and whether or not that would change the way that x would be initialized by int x{}.

This is supremely confusing for me, and I don't remember seeing an answer to this in learncpp (the website that I am using to learn more about C++).

🌐
Reddit
reddit.com › r › ProgrammerHumor › comments › 9ywhi3 › int0_0
r/ProgrammerHumor on Reddit: *(int*)0 = 0;
November 20, 2018 - Actually, C requires that 0 as a pointer type is the null pointer on any machine (regardless of whether the bit representation is all-zeros). Dereferencing the null pointer is undefined behavior in C: while it is not required to segfault, it ...
🌐
PHP
php.net › manual › en › language.types.integer.php
PHP: Integers - Manual
Ints can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8) or binary (base 2) notation. The negation operator can be used to denote a negative int. To use octal notation, precede the number with a 0 (zero). As of PHP 8.1.0, octal notation can also be preceded with 0o or 0O.
Find elsewhere
🌐
Bartleby
bartleby.com › learn › free-expert-answers › is-0-an-integer
Is “0” an Integer? | Free Expert Q&A | bartleby
February 9, 2024 - Integers are whole numbers that can be written without a fractional component. They can be positive-valued or negative-valued. For example, 12 is a positive-valued integer, and −134 is a negative-valued integer. Zero (0) is an integer, but it is neither positive nor negative.
🌐
Wikipedia
en.wikipedia.org › wiki › Integer
Integer - Wikipedia
April 19, 2026 - An integer is the number zero (0), a positive natural number (1, 2, 3, ...), or the negation of a positive natural number (−1, −2, −3, ...). The negations or additive inverses of the positive natural numbers are referred to as negative ...
Top answer
1 of 6
88

It doesn't.

The string terminator is a byte containing all 0 bits.

The unsigned int is two or four bytes (depending on your environment) each containing all 0 bits.

The two items are stored at different addresses. Your compiled code performs operations suitable for strings on the former location, and operations suitable for unsigned binary numbers on the latter. (Unless you have either a bug in your code, or some dangerously clever code!)

But all of these bytes look the same to the CPU. Data in memory (in most currently-common instruction set architectures) doesn't have any type associated with it. That's an abstraction that exists only in the source code and means something only to the compiler.

Edit-added: As an example: It is perfectly possible, even common, to perform arithmetic on the bytes that make up a string. If you have a string of 8-bit ASCII characters, you can convert the letters in the string between upper and lower case by adding or subtracting 32 (decimal). Or if you are translating to another character code you can use their values as indices into an array whose elements provide the equivalent bit coding in the other code.

To the CPU the chars are really extra-short integers. (eight bits each instead of 16, 32, or 64.) To us humans their values happen to be associated with readable characters, but the CPU has no idea of that. It also doesn't know anything about the "C" convention of "null byte ends a string", either (and as many have noted in other answers and comments, there are programming environments in which that convention isn't used at all).

To be sure, there are some instructions in x86/x64 that tend to be used a lot with strings - the REP prefix, for example - but you can just as well use them on an array of integers, if they achieve the desired result.

2 of 6
5

In short there is no difference (except that an int is 2 or 4 bytes wide and a char just 1).

The thing is that all modern libaries either use the null terminator technique or store the length of a string. And in both cases the program/computer knows it reached the end of a string when it either read a null character or it has read as many characters as the size tells it to.

Issues with this start when the null terminator is missing or the length is wrong as then the program starts reading from memory it isn't supposed to.

🌐
Study.com
study.com › math courses › math for kids
Is Zero an Integer? - Lesson | Study.com
July 1, 2020 - Well, if you didn't have zero, you could not have positive or negative integers. Zero is known as the neutral integer, or the integer that's in the middle of the positive and negative integers and has neither a positive nor negative sign.
🌐
Quora
quora.com › Is-0-an-integer
Is 0 an integer? - Quora
Yes 0 is an integer because it does not have a fractional part or component. 0 plus any integer is an integer. Example: 0 + 5 = 5. All numbers from -infinity to +infinity (-∞……..-3,-2,-1, 0, 1,2,3,………+∞) including zero are integers.