Where does C store '\0'? - c

The below code reads a line and return the line length. lim is the length of the array s[].
When the input line length is lim, then s[lim] = '\0'. But the array s[] is only lim-length long, from s[0] to s[lim-1]. Will it cause an buffer overflow? I tested it many times, but the code seemed to work just fine.
int getline(char s[], int lim)
{
int c, i;
for(i = 0; i < lim-1 && ( c = getchar())!= EOF && c!= '\n'; i++)
s[i] = c;
if( c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}

The '\0' is just another character. It is stored right after the last character of the string.
Often, you can "get away" with writing off the end of a buffer with no obvious harm, but don't do it. It's a bug.
I once had to debug a program that contained an error like this. The program was writing a single byte past the end of one buffer. In the debug build, there was enough extra stuff on the stack that the single byte extra caused no harm; the crash only occurred in the release build, but the debugger didn't really work since it was the non-debug build. This is an example of why it is good to test your code both in a "debug" build and in a release build (compiled the way you would give it to your users).

This is a good example as to how to clearly define an interface - its input and returned value;
" int getline(char s[], int lim) "
One possible definition of "lim" is, maximum number of characters to be copied to s[], excluding the terminating null-character i.e. '\0'
Example:
char arr[] = "hello";
getline(arr, strlen(arr));
The other definition of "lim" is, Maximum number of characters to be copied into s[] (including the terminating null-character)
Example:
char arr[] = "hello";
getline(arr, sizeof(arr));
You seem to be supposing the 2nd definition of "lim".

This is a function straight out of "The C Programming Language" by K&R. It's from chapter one. It works because it is correct.
Consider "cat". This is a four character array {'c','a','t','\0'}. The length of the string is 3.
If s[]="cat" then s[0]='c', s[3]='\0'. Eh?
The string length returned by srtlen or what have you is the number of characters minus one. The array is allocated to hold all the 4 characters. That's where the '\0' is, at the end of the array.

No, it won't cause buffer overflow. In fact, a '\0' indicates a NULL position, which is considered as the end of an array. When you go from the beginning to the end of an array, the last position containing the '\0' character will never be considered as a position containing valid data.
You could go over all the array by using while(index < size) as a condition, or by using while(array[position] != NULL)

Related

A null character '\0' at the end of a string

I have the following code.
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int main()
{
int i;
char s[MAXLINE];
for (i = 0; i < 20; ++i) {
s[i] = i + 'A';
printf("%c", s[i]);
}
printf("\nstrlen = %d\n", strlen(s)); // strlen(s): 20
return 0;
}
Should I write
s[i] = '\0';
explicitly after the loop executing to mark the end of the string or it is done automatically? Without s[i] = '\0'; function strlen(s) returns correct value 20.
Yes, you need to add a null terminator yourself. One is not added automatically.
You can verify this by explicitly initializing s to something that doesn't contain a NUL at byte 20.
char s[MAXLINE] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
If you do that strlen(s) won't return 20.
Yes, you should add the null terminator after the loop. Alternatively, you could initialize the entire array with 0. That way, you don't have to add a 0 after the loop because there is one already:
...
char s[MAXLINE] = {0};
...
You MUST add a NUL terminator to mark the end of a C string.
Adding a NUL terminator character isn't automatic (unless documentation states that a function call writes the NUL terminator character for you).
In your case, use:
s[20] = 0;
As mentioned in the comments, C strings are defined by the terminator NUL character. The NUL character is required also by all the strXXX C functions.
If you don't mark the end of the string with a NUL, you have a (binary) sequence of characters, but not a C string. These are sometimes referred to as binary strings and they cannot use the strXXX library functions.
Why do you get Correct Results
It is likely that you get correct results mostly by chance.
The most probable explanation for the correct results is that the OS you are using provides you with a "clean" memory stack (the initial stack memory is all zero)... this isn't always the case.
Since you never wrote on the stack memory prior to executing your code, the following byte is whatever was there before (on your OS, that byte was set to zero when the stack was first initialized).
However, this will not be true if the OS does not provide you with a "clean" stack or if your code runs on a previously used stack.

C dealing with variable length string

I'm new to C, taking a university course.
In one of the tasks I'm given, I deal with strings. I take strings either entered by user or parsed from a file and then use a function on them to produce an answer (if a specific quality exists).
The string can be of variable length but it is acceptable to assume that their maximum length is 80 characters.
I created the program using a
char s[81];
and then filling up the same array with the different strings each time.
Since the string has to be null-terminated I just added a '\0' at index 80;
s[80] = '\0';
But then I got all kind of weird behaviors - Unrelated characters at the end of the string I entered. I assumed this is because there was space between the end of the 'real' characters and the '\0' character filled with garbage(?).
So what I did is I created a function:
void clean_string(char s[], int string_size) {
int index = 0;
while(index < string_size) {
s[index++] = '\0';
}
}
What I call clean, is just filling a string up with zero characters. I do this every time I am done dealing with a string and ready to accept a new one. Then I fill up the string again character by character and when ever I'll stop, the following character will be a '\0' for sure.
To not include any magic numbers in code (81 each time I call clean_string) I used the following:
#define STRING_LENGTH 81
That works for me. The strings show no strange behavior. But I wondered if this is considered bad practice. Are there problems with this approach?
Just emphasizing, I'm not asking for help in the assignment itself, but tips on how to approach these kind of situations better.
Rather than prefilling the entire array with zeros, it should be simple to just add a single zero after you've read all relevant characters.
For example:
char s[STRING_LENGTH];
int c;
int idx = 0;
while (((c = getchar()) != '\n') && (idx < STRING_LENGTH - 1) && (c != EOF)) {
s[idx++] = c;
}
s[idx] = 0;

Initializing end of the string in C

I am learning C now and I'm at the point where I don't really get what is the difference of initializing the end of the string with NULL '\0' character. Below is the example from the book:
#include <stdio.h>
#include <string.h>
int main(){
int i;
char str1[] = "String to copy";
char str2[20];
for(i = 0; str1[i]; i++)
str2[i] = str1[i];
str2[i] = '\0'; //<====WHY ADDING THIS LINE??
printf("String str2 %s\n\n", str2);
return 0;
}
So, why do I have to add NULL character? Because it works without that line as well. Also, is there a difference if I use:
for (i = 0; str1[i]; i++){
str2[i] = str1[i];
}
Thanks for your time.
The line you're referring to is added in general use for safety. When you copy values to a string you always want to be sure that it's null terminated, otherwise when reading the string it will continue past the point where you want the end of that string to be (because it doesn't know where to stop due to lack of the null terminator).
There is no difference with the alternate code you posted since you are separating only the line below the for statement to be in the loop, which happens by default anyway if you don't use the curly braces {}
In C, the end of the string is detected by the null character. Consider the string 'abcd'. If the variable in the actual binary have the next variable immediately after the 'd' character, C will think that the next characters in the platform are part of that string and you will continue. This is called buffer overrun.
Your initial statement allowing 20 bytes for str2 will usually fill it with 20 zeroes, However, this is not required and may not occur. Additionally, let us say you move a 15 character string into str2. Since it starts with 20 zeroes, this will work. However, say that you then copy a 10 character string into str2. The remaining 5 characters will be unchanged and you will then have a 15 character string consisting of the new 10 characters, followed by the five characters previously copied in.
In the code above the for loop says move the character in str1 to str2 and point to the next character. If the character now pointed to in str1 is not 0, loop back and do again. Otherwise drop out of the loop. Now add the null character to the end of the str2. If you left that out, the null character at the end of str1 would not be copied to str2, and you would have no null character at the end of str2.
This can be expressed as
i = 0;
label:
if (str1[i] == 0) goto end;
str2[i] = str1[i];
i = i + 1;
goto label;
end: /* This is the end of the loop*/
Note that the '\0' character has not yet been moved into str2.
Since C requires brackets to show the range of the for, only the first line after the for is part of the loop. If i had local scope and is lost after the loop, you would not be able to just wait to fall out of the loop and make it 0. You would no longer have a valid i pointer to tell you where in str2 you need to add the 0.
An example is C++ or some compilers in C which would allow (syntactically)
for (int i = 0; str1[i]; i++)
{
str2[i] = str1[i];
}
str2[i] = 0;
This would fail because i would be reset to whatever it happened to be before it entered the loop (probably 0) as it falls out of the loop. If it had not been defined before the loop, you would get an undefined variable compiler error.
I see that you fixed the indentation, but had the original indentation stayed there, the following comment would apply.
C does not work solely by indentation (as Python does, for example). If it did, the logic would be as follows and it would fail because str2 would be overwritten as all 0.
for (int i = 0; str1[i]; i++)
{
str2[i] = str1[i];
str2[i] = 0;
}
You should only add a \0 (also called the null byte) in the end of the string. Do as follows:
...
for(i = 0; str1[i]; i++) {
str2[i] = str1[i];
}
str2[i] = '\0'; //<====WHY ADDING THIS LINE??
...
(note that I simply added braces to make the code more readable, it was confusing before)
For me, that is clearer. What you were doing before is basically take advantage of the fact that the integer i that you declared is still available after you ran the loop to add a \0 in the end of str2.
The way strings work in C is that they are basically a pointer to the location of the first character and string functions (such as the ones you can find in string.h) will read every single char until they find a \0 (null byte). It is simply a convention for marking the end of the string.
Some further reading: http://www.cs.nyu.edu/courses/spring05/V22.0201-001/c_tutorial/classes/String.html
'\0' is used for denoting end of string. It is not for the compiler, it is for the libraries and possibly your code. C does not support arrays properly. You can have local arrays, but there is no way to pass them about. If you try you just pass the start address (address of first element). So you can ever have the last element be special e.g. '\0' or always pass the size, being careful not to mess up.
For example:
If your string is like this:
char str[]="Hello \0 World";
will you tell me what would display if you print str ?
Output is:
Hello
This will be the case in character arrays, Hence to be in safer side, it is good to add '\0'at the end of string.
If you didnt add '\0', some garbage values might get printed out, and it will keep on printing till it reached '\0'
In C, char[] do not know the length of the string. It is therefore important character '\0' (ASCII 0) to indicate the end of the string. Your "For" command will not copy '\0', so output is a string > str2 (until found '\ 0' last stop)
Try:
#include <stdio.h>
#include <string.h>
int main(){
int i;
char str[5] = "1234";
str[4] = '5';
printf("String %s\n\n", str);
return 0;
}

C—Infinite loop, I think?

I'm having an issue with a program in C and I think that a for loop is the culprit, but I'm not certain. The function is meant to take a char[] that has already been passed through a reverse function, and write it into a different char[] with all trailing white space characters removed. That is to say, any ' ' or '\t' characters that lie between a '\n' and any other character shouldn't be part of the output.
It works perfectly if there are no trailing white space characters, as in re-writing an exact duplicate of the input char[]. However, if there are any, there is no output at all.
The program is as follows:
#include<stdio.h>
#define MAXLINE 1000
void trim(char output[], char input[], int len);
void reverse(char output[], char input[], int len);
main()
{
int i, c;
int len;
char block[MAXLINE];
char blockrev[MAXLINE];
char blockout[MAXLINE];
char blockprint[MAXLINE];
i = 0;
while ((c = getchar()) != EOF)
{
block[i] = c;
++i;
}
printf("%s", block); // for debugging purposes
reverse(blockrev, block, i); // reverses block for trim function
trim(blockout, blockrev, i);
reverse(blockprint, blockout, i); // reverses it back to normal
// i also have a sneaking suspicion that i don't need this many arrays?
printf("%s", blockprint);
}
void trim(char output[], char input[], int len)
{
int i, j;
i = 0;
j = 0;
while (i <= len)
{
if (input[i] == ' ' || input[i] == '\t')
{
if (i > 0 && input[i-1] == '\n')
for (; input[i] == ' ' || input[i] == '\t'; ++i)
{
}
else
{
output[j] = input[i];
++i;
++j;
}
}
else
{
output[j] = input[i];
++i;
++j;
}
}
}
void reverse(char output[], char input[], int len)
{
int i;
for (i = 0; len - i >= 0; ++i)
{
output[i] = input[len - i];
}
}
I should note that this is a class assignment that doesn't allow the use of string functions, hence why it's so roundabout.
Change
for (i; input[i] == ' ' || input[i] == '\t'; ++i);
to
for (; i <= len && (input[i] == ' ' || input[i] == '\t'); ++i);
With the first method, if the whitespace is at the end, the loop will iterate indefinitely. Not sure how you didn't get an out of bounds access exception, but that's C/C++ for you.
Edit As Arkku brought up in the comments, make sure your character array is still NUL-terminated (the \0 character), and you can check on that case instead. Make sure you're not trimming the NUL character from the end either.
Declaring your main() function simply as main() is an obsolete style that should not be used. The function must be declared either as int main(void) or as int main(int argc, char *argv[]).
Your input process does not null-terminate your input. This means that what you're working with is not a "string", because a C string, by definition, is an array of char that the last element is a null character ('\0'). Instead, what you've got are simple arrays of char. This wouldn't be a problem as long as you're expecting that, and indeed your code is passing array lengths about, but you're also trying to print it with printf(), which requires C strings, not simple arrays of char.
Your reverse() function has an off-by-one error, because you aren't accounting for the fact that C arrays are zero-indexed, so what you're reversing is always one byte longer than your actual input.
What this means is that if you call reverse(output, input, 10), your code will start by assigning the value at input[10] to output[0], but input[10] is one past the end of your data, and since you didn't initialize your arrays before starting to fill them, that's an indeterminate value. In my testing, that indeterminate value happens, coincidentally, to have zero values much of the time, which means that output[0] gets filled with a null ('\0').
You need to be subtracting one more from the index into the input than you actually are. The loop-termination condition in the reverse() function is also wrong, in compensation, that condition should be len - i > 0, not len - i >= 0.
Your trim() function is unnecessarily complex. Additionally, it too has an incorrect loop condition to compensate for the off-by-one error in reverse(). The loop should be while ( i < len ), not while ( i <= len ).
Additionally, the trim() function has the ability to reduce the size of your data, but you don't provide a way to retain that information. (I see in the comments of Arkku's answer that you've corrected for this already. Good.)
Once you've fixed the issue with not keeping track of your data's size changes, and the off-by-one error which is copying indeterminate data (which happens, coincidentally, to be a null) from the end of the blockout array to the beginning of the blockprint array when you do the second reverse(), and you fix the incorrect <= condition in trim() and the incorrect >= condition in reverse(), and you null-terminate your byte array before passing it to printf(), your program will work.
(Moved from comments to an answer)
My guess is that the problem is outside this function, and is caused by the fact that in the described problem cases the output is shorter than the input. Since you are passing the length of the string as an argument, you need to calculate the length of the string after trim, because it may have changed...
For instance, passing an incorrect length to reverse can cause the terminating NUL character (and possibly some leftover whitespace) to end up at the beginning of the string, thus making the output appear empty.
edit: After seeing the edited question with the code of reverse included, in addition to the above problem, your reverse puts the terminating NUL as the first character of the reversed string, which causes it to be the empty string (in some cases your second reverse puts it back, so you don't see it without printing the output of the first reverse). Note that input[len] contains the '\0', not the last character of the string itself.
edit 2: Furthermore, you are not actually terminating the string in block before using it. It may be the case that the uninitialised array often happens to contain zeroes that serve to terminate the string, but for the program to be correct you absolutely need to terminate it with block[i] = '\0'; immediately after the input loop. Similarly you need ensure NUL-termination of the outputs of reverse and trim (in case of trim it seems to me that this already happens as a side-effect of having the loop condition i <= len instead of i < len, but it's not a sign of good code that it's hard to tell).

Why do we need to add a '\0' (null) at the end of a character array in C?

Why do we need to add a '\0' (null) at the end of a character array in C?
I've read it in K&R 2 (1.9 Character Array). The code in the book to find the longest string is as follows :
#include <stdio.h>
#define MAXLINE 1000
int readline(char line[], int maxline);
void copy(char to[], char from[]);
main() {
int len;
int max;
char line[MAXLINE];
char longest[MAXLINE];
max = 0;
while ((len = readline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0)
printf("%s", longest);
return 0;
}
int readline(char s[],int lim) {
int c, i;
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0'; //WHY DO WE DO THIS???
return i;
}
void copy(char to[], char from[]) {
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
My Question is why do we set the last element of the character array as '\0'?
The program works fine without it...
Please help me...
You need to end C strings with '\0' since this is how the library knows where the string ends (and, in your case, this is what the copy() function expects).
The program works fine without it...
Without it, your program has undefined behaviour. If the program happens to do what you expect it to do, you are just lucky (or, rather, unlucky since in the real world the undefined behaviour will choose to manifest itself in the most inconvenient circumstances).
In c "string" means a null terminated array of characters. Compare this with a pascal string which means at most 255 charactes preceeded by a byte indicating the length of the string (but requiring no termination).
Each appraoch has it's pros and cons.
Especially string pointers pointed to array of characters without length known is the only way NULL terminator will determine the length of the string.
Awesome discussion about NULL termination at link
Because C defines a string as contiguous sequence of characters terminated by and including the first null character.
Basically the authors of C had the choice to define a string as a sequence of characters + the length of string or to use a magic marker to delimit the end of the string.
For more information on the subject I suggest to read this article:
"The Most Expensive One-byte Mistake" by Poul-Henning Kamp
http://queue.acm.org/detail.cfm?id=2010365
You have actually written the answer yourself right here:
void copy(char to[], char from[]) {
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
The loop in this function will continue until it encounters a '\0' in the array from. Without a terminating zero the loop will continure an unknown number of steps, until it encounters a zero or an invalid memory region.
Really, you do not need to end a character array by \0. It is the char*, or the C representation of the string that needs to be ended by it.
As for array, you have to add a \0 after its end if you want to transfer it to the string (representer by char*).
On the other hand, you need to have \0 at the end of the array, if you want to address it as char* and plan to use char* functions on it.
'\0' in the array indicates the end of string, which means any character after this character is not considered part of the string.It doesn’t mean they are not part of the character array. i.e., we can still access these characters by indexing but they are just not part when we invoke string related things to this character array.
For a string to be in proper format and be able to work properly with the string functions, it must be a null-terminated character array. Without NULL, the programs show undefined behavior when we invoke string functions on the character array. Even though we might get lucky with the results most of the times, it still is an undefined behavior.
I've just looked it up
If your array is considered as string
Which means like this char array[MAX]="string";
Or like this scanf("%s",array);
Or char* table;
Then the NULL character '\0' will append automatically as the end of the characters on that table
But if you initialized it like this char array[MAX]={'n','o','t','s','t,'r'};
Or you fill it using character by character with %c format
for(int i=0;i<MAX;i++)
scanf("%c",&array[i]);
Or getchar() instead of scanf("%c",...)
Then you have to add '\0' by yourself
Because now it considered as any other array's type (int,float...) So the cases that we consider as empty are actually filled by random numbers or characters depends on the type
Meanwhile in the case of a string type the next character after the last considered character is by default '\0'
for more explanation the length of this char array[]="12345" is 6
The array[5]=='\0' will return 1
by other words you can't define a string array like this char array[3]="123" because we left no room for the '\0' that has to append automatically
last example char array[7]={'t','e','s','t','\0'};
Here array[4] is the NULL character
array[5] and array[6] are random values
But if it was string then "test" array[4] and 5 and 6 are all filled by the NULL character (NULL character can refers to any white_space as I think so tab '\t' and enter '\n' are also NULL characters just like '\0' which may refer to spacebar)
nota ben: we can't assign array[7] or more as we all know but if you try to output it, it'll show a random value as any empty case
It is string terminating symbol,When this is encountered ,compiler comes to know that your string is ended.

Resources