Inspired by this question.
Code:
#include <stdio.h>
int main()
{
char arr[] = "Hello";
char *ptr = arr + 5;
printf("%s\n",ptr);
}
In the above code, I have accessed null-terminated character.
So, What actually happens when accessing null terminated character in literal string? Is it Undefined behaviour?
Essentially, you're passing an empty string as the argument, so it should be treated as such.
For %s conversion specifier, with printf() family
[...]Characters from the array are
written up to (but not including) the terminating null character.[...]
In your case, the null terminator happens to appear at the first element in the array, that's it.
Just for clarification, accessing a null-terminator is OK, accessing a NULL pointer is not OK, and they both are different things!!
You are basically still accessing a null-terminated string.
It is just zero characters long, i.e. it does not contain anything to print.
Your code is basically the same as
printf("");
Compare this, not duplicate but similar question:
Effect of "+1" after the format string parameter to printf()
Nothing particular. A pointer to the null character is interpreted as a zero-length string by functions that expect a string.
Related
This question already has answers here:
What is a null-terminated string?
(7 answers)
Closed last year.
#include <stdio.h>
#include <string.h>
int main()
{
char ch[20] = {'h','i'};
int k=strlen(ch);
printf("%d",k);
return 0;
}
The output is 2.
As far as I know '\0' helps compiler identify the end of string but the output here suggests the strlen can detect the end on it's own then why do we need '\0'?
long story short: it's your compiler making proactive decisions based on the standard.
long story:
char ch[20] = {'h','i'}
in the line above what you are implying to your compiler is;
allocate a memory big enough to store 20 characters (aka, array of 20 chars).
initialize first two slices (first two members of the array) as 'h' & 'i'.
implicitly initialize the rest.
since you are initialing your char array, your compiler is smart enough to insert the null terminator to the third element if it has enough space remaining. This process is the standard for initialization.
if you were to remove the initialization syntax and initialize each member manually like below, the result is undefined behavior.
char ch[20];
ch[0] = 'h';
ch[1] = 'i';
Also, if you were to not have extra space for your compiler to put the null terminator, even if you used a initializer the result would still be an undefined behavior as you can easily test via this code snippet below:
char ch[2] = { 'h','i' };
int k = strlen(ch);
printf("%d\n%s\n", k, ch);
now, if you were to increase the array size of 'ch' from 2 to 3 or any other number higher than 2, you can see that your compiler initializes it with the null terminator thus no more undefined behavior.
In this declaration:
char ch[20] = {'h','i'};
the first two elements are initialized explicitly and all other elements are initialized implicitly by zeroes.
The above declaration in fact (with one exceptions that the third element of the array is also explicitly initialized) is equivalent to:
char ch[20] = "hi";
Pat attention to that the string literal is represented as the following array:
{ 'h', 'i', '\0' }
That is the array contains a string that is terminated by the zero character '\0' and the function strlen can successfully find the length of the stored string.
If you would write for example:
char ch[2] = "hi";
then in this case the array ch does not have a space to store the terminating zero of the string literal. In this case applying the function strlen to this array invokes undefined behavior.
A null byte (i.e. the value 0) is what defines the end of a string in C.
When you defined ch, you gave less initializers than values in the array, so the remaining elements are set to 0. This results in a null terminated string.
The strlen function is basically looking for that value and counting how many elements it sees before it finds the null byte.
As far as I know '\0' helps compiler identify the end of string
Technically, it helps user code and the C runtime library identify the ends of strings. To the extent that the compiler needs to know where strings end, it knows without looking for a terminator.
but the output here suggests the strlen can detect the end on it's own
That would be a misinterpretation. The actual fact is that your string is null-terminated even though you did not put a null terminator in it explicitly. This is a consequence of declaring your array with an initializer that specifies values for only some of the elements. As some of your other answers describe in more detail, that does not produce a partial initialization. Rather, elements for which the initializer does not specify values are default-initialized. For elements of type char, that means initialization with 0, which serves as a string terminator.
Moreover, if the array were without a terminator then the result of passing it to strlen() would be undefined. You could not then conclude anything from the result.
then why do we need '\0'?
So that user code and many standard library functions can recognize the ends of strings. You already know this.
But in many cases we do not need to provide terminators explicitly. In particular, we do not need to represent them in string literals (and it means something different than you probably intended if you do), and you don't need to represent them in the initializers for char arrays storing strings, provided that the array has more elements than you specify in the initializer.
It is likely that your array ch contained zeros thus the byte after i is already set to zero. You can view it with a debugger or simply test it in the code. Trust me, strlen needs the zero to work.
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.
How we use a string name in C to print a string without dereferencing it?
Like here:
char str[];
printf("%s",str);
But in case of arrays we use dereferencing by square brackets[] to print an array:
int a[10];
for(i=0;i<10;i++);
printf("%d",a[i]);
What's the difference between printing a string and an array? We need dereferencing an array by using square brackets [] but in case of string without using any dereferencing printf(); is just printing the value of the string and not the address.
Because that is what the interface to printf dictates.
In case of a "%s" it expects a pointer to a NUL terminated string.
In case of a "%d" it expects an int.
It all depends on what the function (printf or else) expects.
When you use printf with %s, like
printf("%s",str);
printf() wants "a string" which, in C, is the same as "an array of [NUL-terminated] char". So, you pass the only thing that identifies actually the whole array: its name (which decays to the address of the first element).
The same applies to every function that expects an array as argument.
If, to the function you call, you must pass something else, you use different approaches. When printf is used to print an integer, like
printf("%d",a[i]);
you must pass an integer; and an array is not an integer, so you must dereference (or "select") an integer from the array, using the correct notation/mechanism. But things are not different if you want to use a member of a struct, for example. You can not pass printf() a struct, so you use the dot to select a [correct] member of the struct. And the same applies to pointers to integer, you dereference them when you want the value and not the pointer itself. And so on for functions... like
printf("%d", (int) floor(PI));
In the above line, again, if printf wants an integer, you must give it an integer, so to use PI (3.14) you must convert it to an int.
For starters for example such a code snippet
char str[5] = "Hello";
printf("%s",str);
can result in undefined behavior.
The format string "%s" expects that the argument is a pointer to first character of a string.
In C the string is defined as a sequence of characters terminated by the zero character '\0'. That is a character array that contains a string has the sentinel value '\0'.
So in fact such a call of printf
printf("%s",str);
logically is similar to the following code
while ( *str != '\0' ) putchar( *str++ );
In the example above
char str[5] = "Hello";
the character array str does not contain a string because its five elements were initialized by characters of the string literal "Hello" excluding its terminating zero character (there is no enough space in the array to accommodate the sixth character '\0' of the string literal).
So such a call
printf("%s",str);
for the array declared above will try to output anything that follows the array in the memory.
To avoid the undefined behavior you should declare the array for example like
char str[6] = "Hello";
or like
char str[] = "Hello";
As for arrays of other fundamental types then in most cases they do not have a standard sentinel value. Opposite to strings it is impossible to define such a sentinel value for example for integer or float arrays.
So character arrays that contains strings are exclusions from the total kinds of arrays. Strings always end with the terminating zero character '\0' by the definition of the string in C. That is not valid for other kinds of arrays.
Hence to output any array of other kinds you in general need to use a loop knowing how many elements you need to output.
My C codes are listed below:
char s="MIDSH"[3];
printf("%d\n",strlen(&s));
The result of running is 2, which is wrong because char s is just an 'S'.
Does anybody know why and how to solve this problem?
That's actually quite an interesting question. Let's break it up:
"MIDSH"[3]
String literals have array types. So the above applies the subscript operator to the array and evaluates to the 4th character 'S'. It then assigns it to the single character variable s.
printf("%d\n",strlen(&s));
Since s is a single character, and not part of an actual string, the behavior is undefined for the above code.
Signature of strlen is:
size_t strlen(const char *s);
/* The strlen() function calculates the
length of the string s, excluding the
terminating null byte ('\0'). */
strlen expects the input const char array is null terminated. But when you pass the address of an auto variable, you can't guarantee this and thus your program has an undefined behavior.
Does anybody know why and how to solve this problem?
sizeof(char) is guaranteed to be 1. So use sizeof or 1.
The statement
printf("%d\n",strlen(&s));
make no sense for the given case. strlen expects a null terminating string, s is of char type and &s need not necessarily point to an string. What you are getting is one the result of undefined behavior of the program.
To get the size of s you can use sizeof operator
printf("%zu\n", sizeof(s));
The strlen function treats its argument as a pointer to a sequence of characters, where the sequence is terminated by the '\0' character.
By passing a pointer to the single character variable s you effectively say that &s is the first character in such a sequence, but it's not. That means strlen will continue to search in memory under false premises and you will have undefined behavior.
when you use
"char s=" you create a new address on the stack for 's',and this address can't be add or reduce!so though you give strlen a char* but it can't find '\0' by add address.All is wrong.
you should use strlen with a address for char which is a array.like:
char* s = "MIDSH";
printf("%d\n", strlen(s)); //print 5
s++;
printf("%d\n", strlen(s)); //print 4
Is it possible to store the char '\0' inside a char array and then store different characters after? For example
char* tmp = "My\0name\0is\0\0";
I was taught that is actually called a string list in C, but when I tried to print the above (using printf("%s\n", tmp)), it only printed
"My".
Yes, it is surely possible, however, furthermore, you cannot use that array as string and get the content stored after the '\0'.
By definition, a string is a char array, terminated by the null character, '\0'. All string related function will stop at the terminating null byte (for example, an argument, containing a '\0' in between the actual contents, passed to format specifier%s in printf()).
Quoting C11, chapter ยง7.1.1, Definitions of terms
A string is a contiguous sequence of characters terminated by and including the first null
character. [...]
However, for byte-by-byte processing, you're good to go as long as you stay within the allocated memory region.
The problem you are having is with the function you are using to print tmp. Functions like printf will assume that the string is null terminated, so it will stop when it sees the first \0
If you try the following code you will see more of the value in tmp
int main(int c,char** a){
char* tmp = "My\0name\0is\0\0";
write(1,tmp,12);
}