Null and void in C and C++
Do not miss this exclusive book on Binary Tree Problems. Get it now for free.
In C and C++ Programming language, NULL and void have two distinct meanings and use cases like NULL reference, void pointer and much more. We have explored this in depth.
Table of contents:
- What is null
- null or NULL
- Where NULL is used
- NULL reference
- What is void
- void pointer type
- void type in functions
What is null ?
Usually null in a programming language is treated as a value that can be assigned to a variable or an object.
This is different in C as null is an empty address stored by a pointer, i.e. NULL is a variable of pointer type that stores an empty address.
null or NULL
'null' written in lowercases does not exist in C language !
Always write it in uppercases, NULL, that will represent the null character or the 0 binary value
Where NULL is used ?
For example declaring a character variable with an empty value in C language might look like this:
char c = 0x00;
char c = '\x00';
char c = '\0';
Declaring the statement:
char c = NULL;
will give us a warning message:
initialization of ‘char’ from ‘void *’ makes integer from pointer without a cast
and declaring this statement
char c = '';
will give us the error message:
empty character constant
instead, because NULL is a null pointer constant, the next declaration is perfectly valid:
char *c = NULL;
The next statement is also perfectly true.
NULL;
Very important and to be remembered
NULL is a variable that is not part of the compiler.
It is defined in many libraries that need to be imported such as stddef.h.
Another use of null is when we want to find the size of a character string.
In C language the size of a string it is equal with the number of characters + 1 and thus because the last character of the string is the empty or null character.
It is not the same case when declaring a vector of characthers, where the length of the vector is equal with its dimension and there is no need to have null character at the end.
The next statement will have the output of 5
sizeof("text");
Do not make the mistake to confuse the size of a variable with the size of the pointer that points to it !
The next statement will have another output depending on the compiler and operating system that is run onto:
char *c = "text";
printf("size of c=%lu",sizeof(c));
and it is equivalent with the next statement
sizeof(NULL);
NULL reference
We saw earlier that a string is terminated with the NULL. What would happen if by mistake will referencing that memory access ?
Let us take a look at the next example:
char *c = "text";
printf("%c",c[4]);
c = "write";
printf("%c",c[4]);
At the first output we are referencing the element c[4] which is not part of the string "text". As you already know in C language indexing is starting with 0, so the last "t" from the "text" will have its index equal with 3. Surprinsingly in C language this is not an exception, as you might be familiar with. Why ? because we declare the string "text" as a pointer to that string. So, c[4] will have the value of NULL, or a null pointer which marks the end of the string. Referencing c[5], c[6] and so on, it means you'll access the addresses of the locations 5, 6 and so on with values outside the memory alocated for the string "text".
At the second output will get the output of character "e" as we changed the reference of the pointer c to a new text that is larger than the previous one.
The behavior is different from the next example
char c[4] = {'t','e','x','t'};
printf("%c",c[4]);
where we declared a vector of 4 characters and referencing the 5-th one will give us the value outside the memory allocated for the vector.
What is void ?
When we hear about void, we might think at the mathematical concept of an empty set i.e. a set that have no elements in it. In C language we might call that the void set or simply void which represents another type of representation and despite null variable this is a keyword recognized by the compiler and written always in lowercases.
void pointer type
The second uses of void is when we want to declare a pointer that we would not know its type, i.e. a pointer that will store addresses of an unknown type.
We can declare that by using the next statement:
void *p;
To access the elements of this type, the programmer needs to define the arithmetic and addressing mode of these pointers. Let's take the next example:
int v[3] = {1,2,3};
void *p = v;
The next question is how we will access the elements of p ?
We might think that we can use the same notation as we use for v, i.e. instead of v[0],v[1],v[3] we have p[0],p[1],p[3], but the compiler will not know how to access the information because there is no rule for p, so a warning and an error message will be given by compiler:
warning: dereferencing ‘void *’ pointer
error: invalid use of void expression
So, to access the elements of p, we need to:
- first convert the type void * to int *
(int *)p
- calculate the next address from it
*(int *)p + 0
which is echivalent with the addressing of pointers of int type
*v+0 or *v or as we know it v[0]
Note: Even though void is a type you cannot declare a variable of it !
The next statement
void v;
will result in an error message
variable or field ‘v’ declared void
void type in functions
We saw that void is a special pointer type.
What if we are going to use it in a function ?
As you already know, C language is a procedural one, meaning it is working with functions, and more than that with the mathematical concept of a function.
So, what does a function look-alike ?
In mathematics it has a domain and a codomain and a relation between elements that makes every element from the domain to corespond to an element from the codomain.
Now lets extend this principle in programming, we will have the next declaration:
void function_name(void);
This is a prototipe of a function that you can use it latter on and add its definition.
Since we cannot define a variable of type void, the only thing we can do is to declare a pointer of it.
void function_name(void *p);
This statement accepts as domain (or argument) a pointer of type void and as codomain ( or return) a void type.
In this case we can call the function_name by passing in its argument an unknown variable type
#include <stdio.h>
int int_set[3] = {1,2,3};
double real_set[3] = {1.41,2.71,3.14};
char char_set [3] = {'a','b','c'};
void function_name(void *p)
{
if ( p == int_set)
printf("%d %d %d \n", *(int *)p, *(int *)p +1, *(int *)p +2 );
if ( p == real_set)
printf("%f %f %f \n", *(double *)p, *(double *)p +1, *(double *)p +2 );
if ( p == char_set)
printf("%c %c %c \n", *(char *)p, *(char *)p +1, *(char *)p +2 );
}};
int main()
{
void *p;
p=int_set; function_name(p);
p=real_set; function_name(p);
p=char_set; function_name(p);
return 0;
}
The pointer p is initialize one by one with addresses of different global variables types which are then displayed case by case by comparing the address of p with the adress of the global variable.
Note: this might be a simulation of determining the type of a void * as using of sizeof operator will not give the expected behavior.
function_name will not return any value, so the void (i.e. the empty set) is used.
With this article at OpenGenus, you must have the complete idea of Null and void in C.
Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.