Actually, you can use a literal 0 anyplace you would use NULL.
Section 6.3.2.3p3 of the C standard states:
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.
And section 7.19p3 states:
The macros are:
NULLwhich expands to an implementation-defined null pointer constant
So 0 qualifies as a null pointer constant, as does (void *)0 and NULL. The use of NULL is preferred however as it makes it more evident to the reader that a null pointer is being used and not the integer value 0.
Actually, you can use a literal 0 anyplace you would use NULL.
Section 6.3.2.3p3 of the C standard states:
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.
And section 7.19p3 states:
The macros are:
NULLwhich expands to an implementation-defined null pointer constant
So 0 qualifies as a null pointer constant, as does (void *)0 and NULL. The use of NULL is preferred however as it makes it more evident to the reader that a null pointer is being used and not the integer value 0.
NULL is used to make it clear it is a pointer type.
Ideally, the C implementation would define NULL as ((void *) 0) or something equivalent, and programmers would always use NULL when they want a null pointer constant.
If this is done, then, when a programmer has, for example, an int *x and accidentally writes *x = NULL;, then the compiler can recognize that a mistake has been made, because the left side of = has type int, and the right side has type void *, and this is not a proper combination for assignment.
In contrast, if the programmer accidentally writes *x = 0; instead of x = 0;, then the compiler cannot recognize this mistake, because the left side has type int, and the right side has type int, and that is a valid combination.
Thus, when NULL is defined well and is used, mistakes are detected earlier.
In particular answer to your question “Is there a context in which just plain literal 0 would not work exactly the same?”:
- In correct code,
NULLand0may be used interchangeably as null pointer constants. 0will function as an integer (non-pointer) constant, butNULLmight not, depending on how the C implementation defines it.- For the purpose of detecting errors,
NULLand0do not work exactly the same; usingNULLwith a good definition serves to help detect some mistakes that using0does not.
The C standard allows 0 to be used for null pointer constants for historic reasons. However, this is not beneficial except for allowing previously written code to compile in compilers using current C standards. New code should avoid using 0 as a null pointer constant.
Note: This answer applies to the C language, not C++.
Null Pointers
The integer constant literal 0 has different meanings depending upon the context in which it's used. In all cases, it is still an integer constant with the value 0, it is just described in different ways.
If a pointer is being compared to the constant literal 0, then this is a check to see if the pointer is a null pointer. This 0 is then referred to as a null pointer constant. The C standard defines that 0 cast to the type void * is both a null pointer and a null pointer constant.
Additionally, to help readability, the macro NULL is provided in the header file stddef.h. Depending upon your compiler it might be possible to #undef NULL and redefine it to something wacky.
Therefore, here are some valid ways to check for a null pointer:
if (pointer == NULL)
NULL is defined to compare equal to a null pointer. It is implementation defined what the actual definition of NULL is, as long as it is a valid null pointer constant.
if (pointer == 0)
0 is another representation of the null pointer constant.
if (!pointer)
This if statement implicitly checks "is not 0", so we reverse that to mean "is 0".
The following are INVALID ways to check for a null pointer:
int mynull = 0;
<some code>
if (pointer == mynull)
To the compiler this is not a check for a null pointer, but an equality check on two variables. This might work if mynull never changes in the code and the compiler optimizations constant fold the 0 into the if statement, but this is not guaranteed and the compiler has to produce at least one diagnostic message (warning or error) according to the C Standard.
Note that the value of a null pointer in the C language does not matter on the underlying architecture. If the underlying architecture has a null pointer value defined as address 0xDEADBEEF, then it is up to the compiler to sort this mess out.
As such, even on this funny architecture, the following ways are still valid ways to check for a null pointer:
if (!pointer)
if (pointer == NULL)
if (pointer == 0)
The following are INVALID ways to check for a null pointer:
#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)
as these are seen by a compiler as normal comparisons.
Null Characters
'\0' is defined to be a null character - that is a character with all bits set to zero. '\0' is (like all character literals) an integer constant, in this case with the value zero. So '\0' is completely equivalent to an unadorned 0 integer constant - the only difference is in the intent that it conveys to a human reader ("I'm using this as a null character.").
'\0' has nothing to do with pointers. However, you may see something similar to this code:
if (!*char_pointer)
checks if the char pointer is pointing at a null character.
if (*char_pointer)
checks if the char pointer is pointing at a non-null character.
Don't get these confused with null pointers. Just because the bit representation is the same, and this allows for some convenient cross over cases, they are not really the same thing.
References
See Question 5.3 of the comp.lang.c FAQ for more. See this pdf for the C standard. Check out sections 6.3.2.3 Pointers, paragraph 3.
It appears that a number of people misunderstand what the differences between NULL, '\0' and 0 are. So, to explain, and in attempt to avoid repeating things said earlier:
A constant expression of type int with the value 0, or an expression of this type, cast to type void * is a null pointer constant, which if converted to a pointer becomes a null pointer. It is guaranteed by the standard to compare unequal to any pointer to any object or function.
NULL is a macro, defined in as a null pointer constant.
\0 is a construction used to represent the null character, used to terminate a string.
A null character is a byte which has all its bits set to 0.
In C++ NULL expands to 0 or 0L. See this quote from Stroustrup's FAQ:
Should I use NULL or 0?
In C++, the definition of NULL is 0, so there is only an aesthetic difference. I prefer to avoid macros, so I use 0. Another problem with NULL is that people sometimes mistakenly believe that it is different from 0 and/or not an integer. In pre-standard code, NULL was/is sometimes defined to something unsuitable and therefore had/has to be avoided. That's less common these days.
If you have to name the null pointer, call it nullptr; that's what it's called in C++11. Then, "nullptr" will be a keyword.
The downside of NULL in C++ is that it is a define for 0. This is a value that can be silently converted to pointer, a bool value, a float/double, or an int.
That is not very type safe and has lead to actual bugs in an application I worked on.
Consider this:
void Foo(int i);
void Foo(Bar* b);
void Foo(bool b);
main()
{
Foo(0);
Foo(NULL); // same as Foo(0)
}
C++11 defines a nullptr that is convertible to a null pointer but not to other scalars. This is supported in all modern C++ compilers, including VC++ as of 2008. In older versions of GCC there is a similar feature, but then it was called __null.
In C, NULL is a macro that expands either to 0 or (void*)0 (or something that has a similar effect).
In the first case, you can not differentiate between NULL and 0, because they are literally the same.
In the second case, your code will cause a compile error, because you can't compare an integer variable with a pointer.
First some background ...
The macros are
NULLwhich expands to an implementation-defined null pointer constant; C11 §7.19 3
NULL typically is an integer constant 0 or (void*)0 or the like. It may have a different implementation or type - It could be ((int*) 0xDEADBEEF) as strange as that may be.
NULL might be type int. It might be type void * or something else. The type of NULL is not defined.
When the null pointer constant NULL is cast to any pointer, is is a null pointer. An integer 0 cast to a pointer is also a null pointer. A system could have many different (bit-wise) null pointers. They all compare equally to each other. They all compare unequally to any valid object/function. Recall this compare is done as pointers, not integers.
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. C11 §6.3.2.3 3
int x;
if (&x == NULL) ... // this is false
So after all that chapter and verse how to distinguish NULL from 0?
If the macro NULL is defined as an int 0 - it is game over - there is no difference between 0 and NULL.
If NULL is not an int, then code can use _Generic() to differentiate NULL and 0. This does not help OP's "Any change made can only be made within the function itself." requirement as that function accepts an int augment.
If NULL is an int that has a different bit-pattern than 0, then a simple memcmp() can differentiate.
I suspect the whole reason for this exercise is to realize there is no portable method to distinguish NULL from 0.
Your printf call invokes undefined behaviour because s1 doesn't have zero (aka null byte) terminator.
s1 is an array of 4 characters and over writing the null byte is not an issue.
After
s1[3] = 'x';
s1 will become:
[a][b][c][x]
But you can't print it as a string. A string in C is, by definition, a sequence of bytes terminated with a null byte. It just happens to work this time but you should never rely on that.
It means only that after this array there is by accident a null character in the memory.:)
You can try the following example
char s0[] = "xxx";
char s1[] = "abc";
char s2[] = "yyy";
s1[3] = 'x';
printf("%s",s1);
and see the result.
In my experience, tests of the form if (ptr) or if (!ptr) are preferred. They do not depend on the definition of the symbol NULL. They do not expose the opportunity for the accidental assignment. And they are clear and succinct.
Edit: As SoapBox points out in a comment, they are compatible with C++ classes such as unique_ptr, shared_ptr, auto_ptr that are objects that act as pointers and which provide a conversion to bool to enable exactly this idiom. For these objects, an explicit comparison to NULL would have to invoke a conversion to pointer which may have other semantic side effects or be more expensive than the simple existence check that the bool conversion implies.
I have a preference for code that says what it means without unneeded text. if (ptr != NULL) has the same meaning as if (ptr) but at the cost of redundant specificity. The next logical thing is to write if ((ptr != NULL) == TRUE) and that way lies madness. The C language is clear that a boolean tested by if, while or the like has a specific meaning of non-zero value is true and zero is false. Redundancy does not make it clearer.
if (foo) is clear enough. Use it.
The macro NULL is a null-pointer constant and has either an integer type or a pointer type.
Using NULL to assign or initialize a non-pointer variable will lead to question marks from other programmers at the least and it might result in compiler failures.
A line like
int a = NULL;
is not considered good code and it will make the code less readable.
I think @R Sahu's answer reaches the right conclusion, but the supporting evidence it provides (based on a single implementation) is somewhat weak, at best.
This is tagged with both c and c++. The details of how NULL is defined vary between the two, and also varies over time for C++. In all cases, NULL must expand to an "implementation defined null pointer constant". What varies is the definition of "null pointer constant".
In C, a null pointer constant must be an integer literal with the value 0, or the same cast to type "pointer to void"1. An integer with a non-zero value (by itself, or cast to type "pointer to void") is not allowed2. So, 0, 0L and ((void *)0) are all allowed, but something like ((void *)1234) is not.
In C++ 98/03, NULL must also expand to a null pointer constant--but with a somewhat different definition of the term--in particular, casting the integer literal to type "pointer to void" is not allowed in C++ 98/03. This means 0 and 0L are both allowed (and so would '\0' or, if you wanted to be really perverse, (3-(2 + 1)).
In C++ 11, the nullptr_t type and nullptr literal were added to C++, and nullptr is also allowed as a null pointer constant3. NULL is allowed to expand to any null pointer constant, so it could be 0, 0L (etc.) or nullptr, but (again) cannot be any non-zero integer, nor can it have type "pointer to void" (or "pointer to char", etc.)
The intent, however, has always been that NULL only be used to represent a null pointer. Although both C and C++ allow it to be defined as an unadorned 0 (for one example), so it might be possible to assign it to a variable of type int, there's no guarantee that code that does so will even compile (and a fair number of people believe that it shouldn't compile).
Conclusion: you should only ever assign NULL to a pointer. For new code, I'd advise using NULL only in C, and using nullptr in C++. For existing C++ code, I'd convert to using nullptr when any other refactoring is being done on that code, but would not usually modify the code solely to change NULL to nullptr (but code that assigns NULL to any non-pointer type should be corrected immediately, in either C or C++).
1. The wording in the C standard (§6.3.2.3/3) reads:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
The italics (which are in the original) mean that this is considered the definition of that term for this standard.
2. Assigning a null pointer constant to a pointer variable may result in a non-zero value being stored into that variable. Although somewhat unusual, this is perfectly allowable. Even if/when that is the case, however, comparing that variable to a null pointer constant (e.g., NULL or 0) must yield true.
Likewise, an implementation is free to define any number of other integer constants that will produce a null pointer when assigned to a pointer variable. This doesn't affect the requirement on how NULL must be defined.
3. Here, the official wording (from [conv.ptr]) reads:
A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.
Here again, the italics indicate that this is the official definition of the term for that standard.
In C and C++, pointers are inherently unsafe, that is, when you dereference a pointer, it is your own responsibility to make sure it points somewhere valid; this is part of what "manual memory management" is about (as opposed to the automatic memory management schemes implemented in languages like Java, PHP, or the .NET runtime, which won't allow you to create invalid references without considerable effort).
A common solution that catches many errors is to set all pointers that don't point to anything as NULL (or, in correct C++, 0), and checking for that before accessing the pointer. Specifically, it is common practice to initialize all pointers to NULL (unless you already have something to point them at when you declare them), and set them to NULL when you delete or free() them (unless they go out of scope immediately after that). Example (in C, but also valid C++):
void fill_foo(int* foo) {
*foo = 23; // this will crash and burn if foo is NULL
}
A better version:
void fill_foo(int* foo) {
if (!foo) { // this is the NULL check
printf("This is wrong\n");
return;
}
*foo = 23;
}
Without the null check, passing a NULL pointer into this function will cause a segfault, and there is nothing you can do - the OS will simply kill your process and maybe core-dump or pop up a crash report dialog. With the null check in place, you can perform proper error handling and recover gracefully - correct the problem yourself, abort the current operation, write a log entry, notify the user, whatever is appropriate.
The other answers pretty much covered your exact question. A null check is made to be sure that the pointer you received actually points to a valid instance of a type (objects, primitives, etc).
I'm going to add my own piece of advice here, though. Avoid null checks. :) Null checks (and other forms of Defensive Programming) clutter code up, and actually make it more error prone than other error-handling techniques.
My favorite technique when it comes to object pointers is to use the Null Object pattern. That means returning a (pointer - or even better, reference to an) empty array or list instead of null, or returning an empty string ("") instead of null, or even the string "0" (or something equivalent to "nothing" in the context) where you expect it to be parsed to an integer.
As a bonus, here's a little something you might not have known about the null pointer, which was (first formally) implemented by C.A.R. Hoare for the Algol W language in 1965.
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
The definition of NULL is a syntactic crutch to allow a programmer a clear expression that a pointer is, at certain times, pointing nowhere. It's meant for readability - and with increased compiler bloat even for automated checks.
On a hardware level there is no such thing. A pointer is a word, and a word always holds a valid number. So by convention zero was chosen - it could have been any other. Like -1 for example. Selecting 0 offered the advantage that a simple if(pointer) could be used to check if it's a valid pointer (or more correct, not 0).
So my question is what led some C standard to treat the NULL pointer differently from any other pointer?
C also doesn't treat NULL different from any other pointer. The C library in contrast does, but not because of NULL, but rather as its (runtime) value of 0 will make some functions fail, when used as input.
Did K&R want to target an exotic architecture or something?
No. It's a concept needed from a pure software engineering point of view. Having a syntactic construct to handle uninitialized pointers improves readability and possibly enables further checks.
Now, for the historic part, NULL is, like the related handling of TRUE/FALSE inherited from BCPL (through B). Just here it was called nil.
Even with that little historic bit, I'm not sure if Retrocomputing is the right place to ask for this, as it's about a basic concept in software engineering and not anything burdened with historic implication. So a quick search in Software Engineering and Stack Overflow shows that this question has been asked many times. It's something people always stumble on, isn't it?
The important thing you may be missing is that a null pointer in C is not required by the standard to have the same binary representation as the number zero. It is still a "normal" pointer, but it points to a special location that the program is not allowed to use.
The integer constant 0 is turned into nullptr when used as a pointer. Similarly, 0 will become 0.0 when used in floating-point calculations, or false in boolean operations. Coercions are not just one-way: if(ptr) will convert a pointer into a boolean which indicates whether the pointer is not null. All of these conversions serve to avoid requiring that a null pointer shares the same representation as zero.
Most machines represent integer zero as all-bits-zero and comparisons against that are particularly cheap, so it is worthwhile to arrange things so that sentinel values such as null pointers and end-of-string markers are all-bits-zero, and also that +0.0 and false are also all-bits-zero.
There is some subtlety in C in that a literal 0 is converted into nullptr, whereas casting an integer that happens to be zero into a pointer will produce a pointer to location zero. Because they are the same thing on modern platforms, a whole class of potential bugs go away.
I'm assuming you mean the null pointer. It is guaranteed to compare equal to 0.1 But it doesn't have to be represented with all-zero bits.2
See also the comp.lang.c FAQ on null pointers.
- See C99, 6.3.2.3.
- There's no explicit claim; but see the footnote for C99, 7.20.3 (thanks to @birryree in the comments).
§ 6.3.2.3 of the C99 standard 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.
§ 7.17 also says
[...] NULL which expands to an implementation-defined null pointer constant [...]
The address of the NULL pointer might be different from 0, while it will behave like it was in most cases.
(This should be the same as in older C standards, which I don't have at hand right now)
When you have a stack overflow, you are writing in unallocated memory. The behaviour on writing in unallocated memory is undefined, but it will probably make your program crash.
Does that mean we should avoid it in native code too to guard against stack overflow, at least when the size of the array is determined at run time?
Why would you avoid it? I would not avoid it because one benefit
stack declared arrays have as compared to dynamic ones is that
you don't have to bother with memory management issues (e.g., no need to free). This is big plus.
In rare cases if the array you want is too big and won't fit on stack, you can use dynamic arrays. There are some estimates on stack sizes too, on Windows for example in Visual Studio default stack size is 1MB. You can consider this. This size can also be increased if I am not wrong.
The code for pushing (and popping) the stack is wrong. Also, I was confused for a while reading your code because top actually represents array of stack elements, not just the top item.
In particular, malloc expects a number of bytes, however you have passed it a number of elements. And it is poor style to use void * when you mean void **.
I'd suggest making your code more readable by using [] notation and renaming top:
void **new_content = malloc( (stack->size + 1) * sizeof *new_content);
if ( !new_content)
// error handling...
if ( stack->size > 0 )
memcpy( &new_content[1], &stack->content[0], stack->size * sizeof *new_content );
new_content[0] = item;
free(stack->content);
stack->content = new_content;
++stack_size;
Now, this is actually about the worst possible way to implement a stack, in terms of efficiency. Every single push and pop operation you have to do an allocation and copy the entire stack. If you actually make your stack have its "top" at the end, then things become a whole lot simpler:
void **new_content = realloc( stack->content, (stack->size + 1) * sizeof *new_content );
if ( !new_content )
// error handling...
new_content[stack->size++] = item;
stack->content = new_content;
Similar comments apply to your pop function in both varieties.
In stack_push():
void *new_top = malloc(stack->size);
memcpy(new_top + sizeof(void*), stack->top, (stack->size - 1) * sizeof(void *));
This has several problems:
- You are allocating the wrong size. You want to allocate
stack->sizenumber of items times the size of whateverstack->toppoints to. - You must not call
memcpy()whenstack->topis 0. The behaviour is undefined if you copy to or from a null pointer. - You try to perform pointer arithmetic on a
void *. This is a compiler extension (eg. gcc) and is not possible in standard C.
The easiest way to solve these problems is to declare new_top to be the same type as stack->top:
void **old_top = stack->top;
void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->top) {
memcpy (new_top + 1, stack->top, (stack->size - 1) * sizeof *new_top);
}
stack->top = new_top;
Similarly, in stack_pop():
void **old_top = stack->top;
void *item = *stack->top;
void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->size) {
memcpy(new_top, stack->top + 1, stack->size * sizeof *new_top);
}
stack->top = new_top;
If you are seeing large numbers of these null character warnings, consider the following as a possibility. The problem could be caused by someone creating the source file using an editor that saved the file as 16-bit Unicode.
To fix that (on Linux), a hex editor is needed. Simply open up the file in the Geany editor (other editors may also support this too) check the File Properties to see the encoding, if it is a UTF/UCS 16 then in the Document menu you can change it to UTF8. It's probably worth removing the BOM if there is one as well.
In this case the error is to be expected because a UCS16 encoding of characters in the ASCII range will make every second byte a null character.
Somewhere in your source code file you have a character with the byte value 0 (the ASCII NUL character). Which will be invisible in most text editors.
The compiler (GCC) is just telling you that it ignored that character—which really shouldn't be there in your source code.
You could open your file in a hex editor, figure out where that character is and fix it, or delete your source file and copy-paste it back from the code you posted here.