As we know a string terminates with '\0'.
It's because to know the compiler that string ended, or to secure from garbage values.
But how does an array terminate?
If '\0' is used it will take it as 0 a valid integer,
So how does the compiler knows the array ended?
C does not perform bounds checking on arrays. That's part of what makes it fast. However that also means it's up to you to ensure you don't read or write past the end of an array. So the language will allow you to do something like this:
int arr[5];
arr[10] = 4;
But if you do, you invoke undefined behavior. So you need to keep track of how large an array is yourself and ensure you don't go past the end.
Note that this also applies to character arrays, which can be treated as a string if it contains a sequence of characters terminated by a null byte. So this is a string:
char str[10] = "hello";
And so is this:
char str[5] = { 'h', 'i', 0, 0, 0 };
But this is not:
char str[5] = "hello"; // no space for the null terminator.
C doesn't provide any protections or guarantees to you about 'knowing the array is ended.' That's on you as the programmer to keep in mind in order to avoid accessing memory outside your array.
C language does not have native string type. In C, strings are actually one-dimensional array of characters terminated by a null character '\0'.
From C Standard#7.1.1p1 [emphasis mine]
A string is a contiguous sequence of characters terminated by and including the first null character. The term multibyte string is sometimes used instead to emphasize special processing given to multibyte characters contained in the string or to avoid confusion with a wide string. A pointer to a string is a pointer to its initial (lowest addressed) character. The length of a string is the number of bytes preceding the null character and the value of a string is the sequence of the values of the contained characters, in order.
String is a special case of character array which is terminated by a null character '\0'. All the standard library string related functions read the input string based on this rule i.e. read until first null character.
There is no significance of null character '\0' in array of any type apart from character array in C.
So, apart from string, for all other types of array, programmer is suppose to explicitly keep the track of number of elements in the array.
Also, note that, first null character ('\0') is the indication of string termination but it is not stopping you to read beyond it.
Consider this example:
#include <stdio.h>
int main(void) {
char str[5] = {'H', 'i', '\0', 'z'};
printf ("%s\n", str);
printf ("%c\n", str[3]);
return 0;
}
When you print the string
printf ("%s\n", str);
the output you will get is - Hi
because with %s format specifier, printf() writes every byte up to and not including the first null terminator [note the use of null character in the strings], but you can also print the 4th character of array as it is within the range of char array str though beyond first '\0' character
printf ("%c\n", str[3]);
the output you will get is - z
Additional:
Trying to access array beyond its size lead to undefined behavior which includes the program may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.
It’s just a matter of convention. If you wanted to, you could totally write code that handled array termination (for arrays of any type) via some sentinel value. Here’s an example that does just that, arbitrarily using -1 as the sentinel:
int length(int arr[]) {
int i;
for (i = 0; arr[i] != -1; i++) {}
return i;
}
However, this is obviously utterly unpractical: You couldn’t use -1 in the array any longer.
By contrast, for C strings the sentinel value '\0' is less problematic because it’s expected that normal test won’t contain this character. This assumption is kind of valid. But even so there are obviously many strings which do contain '\0' as a valid character, and null-termination is therefore by no means universal.
One very common alternative is to store strings in a struct that looks something like this:
struct string {
unsigned int length;
char *buffer;
}
That is, we explicitly store a length alongside a buffer. This buffer isn’t null-terminated (although in practice it often has an additional terminal '\0' byte for compatibility with C functions).
Anyway, the answer boils down to: For C strings, null termination is a convenient convention. But it is only a convention, enforced by the C string functions (and by the C string literal syntax). You could use a similar convention for other array types but it would be prohibitively impractical. This is why other conventions developed for arrays. Notably, most functions that deal with arrays expect both an array and a length parameter. This length parameter determines where the array terminates.
Related
I understand that strings in C are just character arrays. So I tried the following code, but it gives strange results, such as garbage output or program crashes:
#include <stdio.h>
int main (void)
{
char str [5] = "hello";
puts(str);
}
Why doesn't this work?
It compiles cleanly with gcc -std=c17 -pedantic-errors -Wall -Wextra.
Note: This post is meant to be used as a canonical FAQ for problems stemming from a failure to allocate room for a NUL terminator when declaring a string.
A C string is a character array that ends with a null terminator.
All characters have a symbol table value. The null terminator is the symbol value 0 (zero). It is used to mark the end of a string. This is necessary since the size of the string isn't stored anywhere.
Therefore, every time you allocate room for a string, you must include sufficient space for the null terminator character. Your example does not do this, it only allocates room for the 5 characters of "hello". Correct code should be:
char str[6] = "hello";
Or equivalently, you can write self-documenting code for 5 characters plus 1 null terminator:
char str[5+1] = "hello";
But you can also use this and let the compiler do the counting and pick the size:
char str[] = "hello"; // Will allocate 6 bytes automatically
When allocating memory for a string dynamically in run-time, you also need to allocate room for the null terminator:
char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
If you don't append a null terminator at the end of a string, then library functions expecting a string won't work properly and you will get "undefined behavior" bugs such as garbage output or program crashes.
The most common way to write a null terminator character in C is by using a so-called "octal escape sequence", looking like this: '\0'. This is 100% equivalent to writing 0, but the \ serves as self-documenting code to state that the zero is explicitly meant to be a null terminator. Code such as if(str[i] == '\0') will check if the specific character is the null terminator.
Please note that the term null terminator has nothing to do with null pointers or the NULL macro! This can be confusing - very similar names but very different meanings. This is why the null terminator is sometimes referred to as NUL with one L, not to be confused with NULL or null pointers. See answers to this SO question for further details.
The "hello" in your code is called a string literal. This is to be regarded as a read-only string. The "" syntax means that the compiler will append a null terminator in the end of the string literal automatically. So if you print out sizeof("hello") you will get 6, not 5, because you get the size of the array including a null terminator.
It compiles cleanly with gcc
Indeed, not even a warning. This is because of a subtle detail/flaw in the C language that allows character arrays to be initialized with a string literal that contains exactly as many characters as there is room in the array and then silently discard the null terminator (C17 6.7.9/15). The language is purposely behaving like this for historical reasons, see Inconsistent gcc diagnostic for string initialization for details. Also note that C++ is different here and does not allow this trick/flaw to be used.
From the C Standard (7.1.1 Definitions of terms)
1 A string is a contiguous sequence of characters terminated by and
including the first null character. The term multibyte string is
sometimes used instead to emphasize special processing given to
multibyte characters contained in the string or to avoid confusion
with a wide string. A pointer to a string is a pointer to its initial
(lowest addressed) character. The length of a string is the number of
bytes preceding the null character and the value of a string is the
sequence of the values of the contained characters, in order.
In this declaration
char str [5] = "hello";
the string literal "hello" has the internal representation like
{ 'h', 'e', 'l', 'l', 'o', '\0' }
so it has 6 characters including the terminating zero. Its elements are used to initialize the character array str which reserve space only for 5 characters.
The C Standard (opposite to the C++ Standard) allows such an initialization of a character array when the terminating zero of a string literal is not used as an initializer.
However as a result the character array str does not contain a string.
If you want that the array would contain a string you could write
char str [6] = "hello";
or just
char str [] = "hello";
In the last case the size of the character array is determined from the number of initializers of the string literal that is equal to 6.
Can all strings be considered an array of characters (Yes), can all character arrays be considered strings (No).
Why Not? and Why does it matter?
In addition to the other answers explaining that the length of a string is not stored anywhere as part of the string and the references to the standard where a string is defined, the flip-side is "How do the C library functions handle strings?"
While a character array can hold the same characters, it is simply an array of characters unless the last character is followed by the nul-terminating character. That nul-terminating character is what allows the array of characters to be considered (handled as) a string.
All functions in C that expect a string as an argument expect the sequence of characters to be nul-terminated. Why?
It has to do with the way all string functions work. Since the length isn't included as part of an array, string-functions, scan forward in the array until the nul-character (e.g. '\0' -- equivalent to decimal 0) is found. See ASCII Table and Description. Regardless whether you are using strcpy, strchr, strcspn, etc.. All string functions rely on the nul-terminating character being present to define where the end of that string is.
A comparison of two similar functions from string.h will emphasize the importance of the nul-terminating character. Take for example:
char *strcpy(char *dest, const char *src);
The strcpy function simply copies bytes from src to dest until the nul-terminating character is found telling strcpy where to stop copying characters. Now take the similar function memcpy:
void *memcpy(void *dest, const void *src, size_t n);
The function performs a similar operation, but does not consider or require the src parameter to be a string. Since memcpy cannot simply scan forward in src copying bytes to dest until a nul-terminating character is reached, it requires an explicit number of bytes to copy as a third parameter. This third parameter provides memcpy with the same size information strcpy is able to derive simply by scanning forward until a nul-terminating character is found.
(which also emphasizes what goes wrong in strcpy (or any function expecting a string) if you fail to provide the function with a nul-terminated string -- it has no idea where to stop and will happily race off across the rest of your memory segment invoking Undefined Behavior until a nul-character just happens to be found somewhere in memory -- or a Segmentation Fault occurs)
That is why functions expecting a nul-terminated string must be passed a nul-terminated string and why it matters.
Intuitively...
Think of an array as a variable (holds things) and a string as a value (can be placed in a variable).
They are certainly not the same thing. In your case the variable is too small to hold the string, so the string gets cut off. ("quoted strings" in C have an implicit null character at the end.)
However it's possible to store a string in an array that is much larger than the string.
Note that the usual assignment and comparison operators (= == < etc.) don't work as you might expect. But the strxyz family of functions comes pretty close, once you know what you're doing. See the C FAQ on strings and arrays.
I understand that strings in C are just character arrays. So I tried the following code, but it gives strange results, such as garbage output or program crashes:
#include <stdio.h>
int main (void)
{
char str [5] = "hello";
puts(str);
}
Why doesn't this work?
It compiles cleanly with gcc -std=c17 -pedantic-errors -Wall -Wextra.
Note: This post is meant to be used as a canonical FAQ for problems stemming from a failure to allocate room for a NUL terminator when declaring a string.
A C string is a character array that ends with a null terminator.
All characters have a symbol table value. The null terminator is the symbol value 0 (zero). It is used to mark the end of a string. This is necessary since the size of the string isn't stored anywhere.
Therefore, every time you allocate room for a string, you must include sufficient space for the null terminator character. Your example does not do this, it only allocates room for the 5 characters of "hello". Correct code should be:
char str[6] = "hello";
Or equivalently, you can write self-documenting code for 5 characters plus 1 null terminator:
char str[5+1] = "hello";
But you can also use this and let the compiler do the counting and pick the size:
char str[] = "hello"; // Will allocate 6 bytes automatically
When allocating memory for a string dynamically in run-time, you also need to allocate room for the null terminator:
char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
If you don't append a null terminator at the end of a string, then library functions expecting a string won't work properly and you will get "undefined behavior" bugs such as garbage output or program crashes.
The most common way to write a null terminator character in C is by using a so-called "octal escape sequence", looking like this: '\0'. This is 100% equivalent to writing 0, but the \ serves as self-documenting code to state that the zero is explicitly meant to be a null terminator. Code such as if(str[i] == '\0') will check if the specific character is the null terminator.
Please note that the term null terminator has nothing to do with null pointers or the NULL macro! This can be confusing - very similar names but very different meanings. This is why the null terminator is sometimes referred to as NUL with one L, not to be confused with NULL or null pointers. See answers to this SO question for further details.
The "hello" in your code is called a string literal. This is to be regarded as a read-only string. The "" syntax means that the compiler will append a null terminator in the end of the string literal automatically. So if you print out sizeof("hello") you will get 6, not 5, because you get the size of the array including a null terminator.
It compiles cleanly with gcc
Indeed, not even a warning. This is because of a subtle detail/flaw in the C language that allows character arrays to be initialized with a string literal that contains exactly as many characters as there is room in the array and then silently discard the null terminator (C17 6.7.9/15). The language is purposely behaving like this for historical reasons, see Inconsistent gcc diagnostic for string initialization for details. Also note that C++ is different here and does not allow this trick/flaw to be used.
From the C Standard (7.1.1 Definitions of terms)
1 A string is a contiguous sequence of characters terminated by and
including the first null character. The term multibyte string is
sometimes used instead to emphasize special processing given to
multibyte characters contained in the string or to avoid confusion
with a wide string. A pointer to a string is a pointer to its initial
(lowest addressed) character. The length of a string is the number of
bytes preceding the null character and the value of a string is the
sequence of the values of the contained characters, in order.
In this declaration
char str [5] = "hello";
the string literal "hello" has the internal representation like
{ 'h', 'e', 'l', 'l', 'o', '\0' }
so it has 6 characters including the terminating zero. Its elements are used to initialize the character array str which reserve space only for 5 characters.
The C Standard (opposite to the C++ Standard) allows such an initialization of a character array when the terminating zero of a string literal is not used as an initializer.
However as a result the character array str does not contain a string.
If you want that the array would contain a string you could write
char str [6] = "hello";
or just
char str [] = "hello";
In the last case the size of the character array is determined from the number of initializers of the string literal that is equal to 6.
Can all strings be considered an array of characters (Yes), can all character arrays be considered strings (No).
Why Not? and Why does it matter?
In addition to the other answers explaining that the length of a string is not stored anywhere as part of the string and the references to the standard where a string is defined, the flip-side is "How do the C library functions handle strings?"
While a character array can hold the same characters, it is simply an array of characters unless the last character is followed by the nul-terminating character. That nul-terminating character is what allows the array of characters to be considered (handled as) a string.
All functions in C that expect a string as an argument expect the sequence of characters to be nul-terminated. Why?
It has to do with the way all string functions work. Since the length isn't included as part of an array, string-functions, scan forward in the array until the nul-character (e.g. '\0' -- equivalent to decimal 0) is found. See ASCII Table and Description. Regardless whether you are using strcpy, strchr, strcspn, etc.. All string functions rely on the nul-terminating character being present to define where the end of that string is.
A comparison of two similar functions from string.h will emphasize the importance of the nul-terminating character. Take for example:
char *strcpy(char *dest, const char *src);
The strcpy function simply copies bytes from src to dest until the nul-terminating character is found telling strcpy where to stop copying characters. Now take the similar function memcpy:
void *memcpy(void *dest, const void *src, size_t n);
The function performs a similar operation, but does not consider or require the src parameter to be a string. Since memcpy cannot simply scan forward in src copying bytes to dest until a nul-terminating character is reached, it requires an explicit number of bytes to copy as a third parameter. This third parameter provides memcpy with the same size information strcpy is able to derive simply by scanning forward until a nul-terminating character is found.
(which also emphasizes what goes wrong in strcpy (or any function expecting a string) if you fail to provide the function with a nul-terminated string -- it has no idea where to stop and will happily race off across the rest of your memory segment invoking Undefined Behavior until a nul-character just happens to be found somewhere in memory -- or a Segmentation Fault occurs)
That is why functions expecting a nul-terminated string must be passed a nul-terminated string and why it matters.
Intuitively...
Think of an array as a variable (holds things) and a string as a value (can be placed in a variable).
They are certainly not the same thing. In your case the variable is too small to hold the string, so the string gets cut off. ("quoted strings" in C have an implicit null character at the end.)
However it's possible to store a string in an array that is much larger than the string.
Note that the usual assignment and comparison operators (= == < etc.) don't work as you might expect. But the strxyz family of functions comes pretty close, once you know what you're doing. See the C FAQ on strings and arrays.
I understand that strings in C are just character arrays. So I tried the following code, but it gives strange results, such as garbage output or program crashes:
#include <stdio.h>
int main (void)
{
char str [5] = "hello";
puts(str);
}
Why doesn't this work?
It compiles cleanly with gcc -std=c17 -pedantic-errors -Wall -Wextra.
Note: This post is meant to be used as a canonical FAQ for problems stemming from a failure to allocate room for a NUL terminator when declaring a string.
A C string is a character array that ends with a null terminator.
All characters have a symbol table value. The null terminator is the symbol value 0 (zero). It is used to mark the end of a string. This is necessary since the size of the string isn't stored anywhere.
Therefore, every time you allocate room for a string, you must include sufficient space for the null terminator character. Your example does not do this, it only allocates room for the 5 characters of "hello". Correct code should be:
char str[6] = "hello";
Or equivalently, you can write self-documenting code for 5 characters plus 1 null terminator:
char str[5+1] = "hello";
But you can also use this and let the compiler do the counting and pick the size:
char str[] = "hello"; // Will allocate 6 bytes automatically
When allocating memory for a string dynamically in run-time, you also need to allocate room for the null terminator:
char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
If you don't append a null terminator at the end of a string, then library functions expecting a string won't work properly and you will get "undefined behavior" bugs such as garbage output or program crashes.
The most common way to write a null terminator character in C is by using a so-called "octal escape sequence", looking like this: '\0'. This is 100% equivalent to writing 0, but the \ serves as self-documenting code to state that the zero is explicitly meant to be a null terminator. Code such as if(str[i] == '\0') will check if the specific character is the null terminator.
Please note that the term null terminator has nothing to do with null pointers or the NULL macro! This can be confusing - very similar names but very different meanings. This is why the null terminator is sometimes referred to as NUL with one L, not to be confused with NULL or null pointers. See answers to this SO question for further details.
The "hello" in your code is called a string literal. This is to be regarded as a read-only string. The "" syntax means that the compiler will append a null terminator in the end of the string literal automatically. So if you print out sizeof("hello") you will get 6, not 5, because you get the size of the array including a null terminator.
It compiles cleanly with gcc
Indeed, not even a warning. This is because of a subtle detail/flaw in the C language that allows character arrays to be initialized with a string literal that contains exactly as many characters as there is room in the array and then silently discard the null terminator (C17 6.7.9/15). The language is purposely behaving like this for historical reasons, see Inconsistent gcc diagnostic for string initialization for details. Also note that C++ is different here and does not allow this trick/flaw to be used.
From the C Standard (7.1.1 Definitions of terms)
1 A string is a contiguous sequence of characters terminated by and
including the first null character. The term multibyte string is
sometimes used instead to emphasize special processing given to
multibyte characters contained in the string or to avoid confusion
with a wide string. A pointer to a string is a pointer to its initial
(lowest addressed) character. The length of a string is the number of
bytes preceding the null character and the value of a string is the
sequence of the values of the contained characters, in order.
In this declaration
char str [5] = "hello";
the string literal "hello" has the internal representation like
{ 'h', 'e', 'l', 'l', 'o', '\0' }
so it has 6 characters including the terminating zero. Its elements are used to initialize the character array str which reserve space only for 5 characters.
The C Standard (opposite to the C++ Standard) allows such an initialization of a character array when the terminating zero of a string literal is not used as an initializer.
However as a result the character array str does not contain a string.
If you want that the array would contain a string you could write
char str [6] = "hello";
or just
char str [] = "hello";
In the last case the size of the character array is determined from the number of initializers of the string literal that is equal to 6.
Can all strings be considered an array of characters (Yes), can all character arrays be considered strings (No).
Why Not? and Why does it matter?
In addition to the other answers explaining that the length of a string is not stored anywhere as part of the string and the references to the standard where a string is defined, the flip-side is "How do the C library functions handle strings?"
While a character array can hold the same characters, it is simply an array of characters unless the last character is followed by the nul-terminating character. That nul-terminating character is what allows the array of characters to be considered (handled as) a string.
All functions in C that expect a string as an argument expect the sequence of characters to be nul-terminated. Why?
It has to do with the way all string functions work. Since the length isn't included as part of an array, string-functions, scan forward in the array until the nul-character (e.g. '\0' -- equivalent to decimal 0) is found. See ASCII Table and Description. Regardless whether you are using strcpy, strchr, strcspn, etc.. All string functions rely on the nul-terminating character being present to define where the end of that string is.
A comparison of two similar functions from string.h will emphasize the importance of the nul-terminating character. Take for example:
char *strcpy(char *dest, const char *src);
The strcpy function simply copies bytes from src to dest until the nul-terminating character is found telling strcpy where to stop copying characters. Now take the similar function memcpy:
void *memcpy(void *dest, const void *src, size_t n);
The function performs a similar operation, but does not consider or require the src parameter to be a string. Since memcpy cannot simply scan forward in src copying bytes to dest until a nul-terminating character is reached, it requires an explicit number of bytes to copy as a third parameter. This third parameter provides memcpy with the same size information strcpy is able to derive simply by scanning forward until a nul-terminating character is found.
(which also emphasizes what goes wrong in strcpy (or any function expecting a string) if you fail to provide the function with a nul-terminated string -- it has no idea where to stop and will happily race off across the rest of your memory segment invoking Undefined Behavior until a nul-character just happens to be found somewhere in memory -- or a Segmentation Fault occurs)
That is why functions expecting a nul-terminated string must be passed a nul-terminated string and why it matters.
Intuitively...
Think of an array as a variable (holds things) and a string as a value (can be placed in a variable).
They are certainly not the same thing. In your case the variable is too small to hold the string, so the string gets cut off. ("quoted strings" in C have an implicit null character at the end.)
However it's possible to store a string in an array that is much larger than the string.
Note that the usual assignment and comparison operators (= == < etc.) don't work as you might expect. But the strxyz family of functions comes pretty close, once you know what you're doing. See the C FAQ on strings and arrays.
In C, when I initialize my array this way:
char full_name[] = {
't', 'o', 'a', 'n'
};
and print it with printf("%s", full_name);
and run it with valgrind I got error
Uninitialised value was create by stack allocation
Why does that happen?
Since %s format specifier expects a null-terminated string, the resulting behavior of your code is undefined. Your program is considered ill-formed, and can produce any output at all, produce no output, crash, and so on. To put this shortly, don't do that.
This is not to say that all arrays of characters must be null-terminated: the rule applies only to arrays of characters intended to use as C strings, e.g. to be passed to printf on %s format specifier, or to be passed to strlen or other string functions of the Standard C library.
If you are intended to use your char array for something else, it does not need to be null terminated. For example, this use is fully defined:
char full_name[] = {
't', 'o', 'a', 'n'
};
for (size_t i = 0 ; i != sizeof(full_name) ; i++) {
printf("%c", full_name[i]);
}
If you do not provide the '\0' at the end for the comma separated brace enclosed initializer list, technically, full_name is not a string, as the char array is not null-terminated.
Just to clear things out a bit, unlike the initializer being string literal, a comma separated list does not automatically count and put the terminating null character into the array.
So, in case of a definition like
char full_name[] = {
't', 'o', 'a', 'n'
};
the size of the array is 4 and it has 't', 'o', 'a', 'n' into it.
OTOH, in case of
char full_name[] = "toan";
full_name will be of size 5 and will contain 't', 'o', 'a', 'n' and '\0'into it.
When you try to make use of the former array with any function operating on strings (i.e., expects a null-terminated char array), you'll get undefined behavior as most of the string functions will go out of bound in search for the null-terminator.
In your particular example, for %s format specifier with printf(), quoting the C11 standard, chapter §7.21.6.1, fprintf() function description (emphasis mine)
s If no l length modifier is present, the argument shall be a pointer to the initial
element of an array of character type.280) Characters from the array are
written up to (but not including) the terminating null character. If the
precision is specified, no more than that many bytes are written. If the
precision is not specified or is greater than the size of the array, the array shall
contain a null character.
That means, the printf() will look for a null-terminator to mark/understand the end of the array. In your example, the lack of the null-terminator will cause printf() to go beyond the allocated memory (full_name[3]) and access out-of-bound memory (full_name[4]) which will cause the UB.
If you use a non-null-terminated char sequence as a string, C functions will just keep going. It's the '\0' that tells them to stop. So, whatever happens to be in memory after the sequence will be taken as part of the string. This may eventually cross a memory boundary and cause an error, or it may just print gibberish if it happens to find a '\0' somewhere and stop.
printf will interpret "%s" as a standard C string. This means that the code that is generated will simply keep reading characters until it finds a null terminator (\0).
Often this will mean this wandering pointer will venture into uncharted memory and Valgrind will notice this as an error.
You have to explicitly add your own null terminator when initialising a char array, if you intend to use it as a string at some point.
Before passing the instruction pointer to a function expecting a c string you are implicitly entering a legally binding contract with that code block. In the primary section of this contract both parties agree to refrain from exchanging dedicated string length information and assert that all passed parameters declared as strings point to a sequence of characters terminated by \0 which gives each party the option to calculate the length.
If you don't include a terminating \0 you will commit a fundamental breach of contract.
The OS court will randomly sue your executable with madness or even death.
Why don't the numeric arrays end with a null character?
For example,
char name[] = {'V', 'I', 'J', 'A', 'Y', '\0'};
But in case of numeric arrays there is no sign of null character at the end...
For example,
int marks[] = {20, 22, 23};
What is the reason behind that?
The question asked contains a hidden assumption, that all char arrays do end with a null character. This is in fact not always the case: this char array does not end with \0:
char no_zero[] = { 'f', 'o', 'o' };
The char arrays that must end with the null character are those meant for use as strings, which indeed require termination.
In your example, the char array only ends with a null character because you made it so. The single place where the compiler will insert the null character for you is when declaring a char array from a string literal, such as:
char name[] = "VIJAY";
// the above is sugar for:
char name[] = { 'V', 'I', 'J', 'A', 'Y', '\0' };
In that case, the null character is inserted automatically to make the resulting array a valid C string. No such requirement exists for arrays of other numeric types, nor can they be initialized from a string literal. In other words, appending a zero to a numeric array would serve no purpose whatsoever, because there is no code out there that uses the zero to look for the array end, since zero is a perfectly valid number.
Arrays of pointers are sometimes terminated with a NULL pointer, which makes sense because a NULL pointer cannot be confused with a valid pointer. The argv array of strings, received by main(), is an example of such an array.
An array can end in anything that is a valid value of the array element type. But only a \0 terminated char array is called a string.
For example
char name[]={'V','I','J','A','Y'};
Valid, but not a string, the limit is that you can't use it in functions expecting a string like strlen etc.
To clarify from OP's comment below, by the C standard, any character literals like 'a', '1', etc, including '\0' are type int. And you can put a '\0' at the end of an int array like this:
int arr[] = {20, 22, 23, '\0'};
But people usually don't do that because it's conventional that '\0' is only used to terminated strings. The above code is equivalent to
int arr[] = {20, 22, 23, 0};
A string ends with a 0 terminator, but a string is not the same thing as an array. We use arrays to store strings, but we also use arrays to store things that are not strings. That's why arrays in general don't automatically have a 0 appended to them.
Besides, in any generic array of int, 0 may be a valid (non-sentinel) value.
You can make an int array end with 0 as well, if you wish:
int iarray[] = {1, 2, 3, 0};
Since '\0' and 0 are exactly the same, you could even replace the 0 above by '\0'.
Your confusion might be due to the automatic insertion of '\0' in a declaration such as:
char s[] = "hello";
In the above, the definition of s is equivalent to char s[] = {'h', 'e', 'l', 'l', 'o', '\0'};. Think of this a a convenient shortcut provided by the C standard. If you want, you can force a non-zero terminated char array by being explicit about the size:
char s[5] = "hello";
In the above example, s won't be NUL terminated.
Also note that character literals in C are of type int, so '\0' is actually an int. (Also further, char is an integral type.)
There are three, maybe four decent ways of tracking an array's length, only two of which are common in C:
Keep track of the length yourself and pass it along with the pointer.
This is how arrays typically work. It doesn't require any special formatting, and makes sub-array views trivial to represent. (Add to the pointer, subtract from the length, and there ya go.)
Any function in the standard library that works with non-string arrays expects this already. And even some functions that mess with strings (like strncat or fgets) do it for safety's sake.
Terminate the array with some "sentinel" value.
This is how C strings work. Because nearly every character set/encoding in existence defines '\0' as a non-printable, "do nothing" control character, it's thus not a typical part of text, so using it to terminate a string makes sense.
Note that when you're using a char[] as a byte array, though, you still have to specify a length. That's because bytes aren't characters. Once you're dealing with bytes rather than characters, 0 loses its meaning as a sentinel value and goes back to being plain old data.
The big issue is that with most fundamental types, every possible arrangement of sizeof(type) bytes might represent a valid, useful value. For integral values, zero is particularly common; it is probably one of the most used and most useful numbers in all of computing. I fully expect to be able to put a 0 into an array of integers without appearing to lose half my data.
So then the question becomes, what would be a good sentinel value? What otherwise-legal number should be outlawed in arrays? And that question has no good, universal answer; it depends entirely on your data. So if you want to do such a thing, you're on your own.
Besides the lack of a decent sentinel value, this approach falls flat with non-character types for another reason: it's more complicated to represent subsets of the array. In order for a recursive function to pass part of the array to itself, it would have to insert the sentinel value, call itself, and then restore the old value. Either that, or it could pass a pointer to the start of the range and the length of the range. But wait...isn't that what you are trying to avoid? :P
For completeness, the two other methods:
Create a struct that can store the array's length along with a pointer to the data.
This is a more object-oriented approach, and is how arrays work in nearly every modern language (and how vectors work in C++). It works OK in C iff you have an API to manage such structs, and iff you use that API religiously. (Object-oriented languages provide a way to attach the API to the object itself. C doesn't, so it's up to you to stick to the API.) But any function that wasn't designed to work with your structs will need to be passed a pointer (and possibly a length) using one of the above two methods.
Pass two pointers.
This is a common way to pass a "range" in C++. You pass a pointer to the start of the array, and a pointer just past the end. It's less common in C, though, because with raw pointers, (start,length) and (start,end) represent the same data -- and C doesn't have the iterators and templates that make this so much more useful.
You need to end C strings with '\0' since this is how the library knows where the string ends.
The NUL-termination is what differentiates a char array from a string (a NUL-terminated char-array). Most string-manipulating functions relies on NUL to know when the string is finished (and its job is done), and won't work with simple char-array (eg. they will keep on working past the boundaries of the array, and continue until it finds a NUL somewhere in memory - often corrupting memory as it goes).
You do not have to have a '\0' char at at the end of character array! This is a wrong assumption. There is no rule which says you do. Characters (char type) are exactly like any other kind of data.
You do have to have a null terminated char array if you want to print the array using standard printf-family functions. But only because those functions depend on the ending of the character array - '\0' char.
Functions often have rules concerning the kind of data they expect. String (char[]) functions are not exception. But this is not a language requirement, it's the API you're using which has these requirements.
Char array ends with special char '\0' so that it can be treated as string.
And when you are manipulating string there must be some way to tell the length(boundary) of that string.
Look at function prototype of strcpy
char * strcpy ( char * destination, const char * source );
How does it know to copy how many chars from source to destination? The answer is by looking at the position of '\0'.
The '\0' char is prominent when dealing with string as a char *. Without '\0' as an end marker you wouldn't have been able to treat char * as string.
Arrays by themselves do not have to be 0\ terminated, it is the usage of the character arrays in a specific way that needs them to be \0 terminated. The standard library functions which act on character arrays will use the \0 to detect end of the array and hence treat it as a string, this behavior means the users of these functions will need to follow the \0 termination precondition. If your character array usage doesn't use any such functionality then it doesn't need the \0 terminator.
An array of char not necessarilly ends with \0.
It is a C convention that strings are ended with \0.
This is useful to find the end of the string.
But if you are only interested in holding data that is of type char, you can have a \0 at end or not.
If your array of char is intended to be used as a string, you should add \0 at the end of it.
EDIT: What is ended by \0 are the string literals, not the array of char.
The question is ill formulated.
An example to take the point that how \0 will confuse if taken in a integer array:-
int marks[]={20,22,23,0,93,'\0'};
^
So now your array will assume that 0(marked) is an end of the array which is not true.
\0 is usually used to terminate a string. In string \0 is regarded as the end of the string.
In your example you dont need to terminate it with '\0'
Found a very interesting wiki post:-
At the time C (and the languages that it was derived from) was
developed, memory was extremely limited, so using only one byte of
overhead to store the length of a string was attractive. The only
popular alternative at that time, usually called a "Pascal string"
(though also used by early versions of BASIC), used a leading byte to
store the length of the string. This allows the string to contain NUL
and made finding the length need only one memory access (O(1)
(constant) time). However, C designer Dennis Ritchie chose to follow
the convention of NUL-termination, already established in BCPL, to
avoid the limitation on the length of a string caused by holding the
count in an 8- or 9-bit slot, and partly because maintaining the count
seemed, in our experience, less convenient than using a terminator.
Also check a related post:- nul terminating a int array
We have a convention: special character '0' with numeric code 0, marks end of the string.
But if you want to mark end of int array, how will you know is that 0 is a valid array member or end-of-array mark? So, in general, it is not possible to have such a mark.
In other words:
The character '\0' (but not character '0', code 48) have no any sense in context of text string (by convention, it is a special character, that marks end), so it can be used as end-of-array mark:
Integer 0 or \0 (which is the same), are valid integer. It can have sense, and that is why it cannot be used as end-of-array mark:
int votesInThisThread[] = { 0, -1, 5, 0, 2, 0 }; // Zeroes here is a valid numbers of upvotes
If you'l try to detect end of this example array by searching 0, you'll get size of 0.
That is what question about?