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.
The goal is to count all the vowels from a char* the user puts in. The program has other functions and this is called from main.
I have also included stdio.h, stdbool.h, and string.h
char* countWord;
int vowels;
printf("Type the word to count vowels:");
scanf("%s", &countWord);
vowels = vowelCount(countWord);
printf("%d", vowels);
The following is the function I was used. I also tried strlen(string) which caused a crash as well.
int vowelCount(char* string){
int vowels;
int i;
int size;
printf("function entered");
for (; *string; string++){
if (string[i] == 'a'){
vowels++;
} else if(string[i] == 'e'){
vowels++;
} else if(string[i] == 'i'){
vowels++;
} else if(string[i] == 'o'){
vowels++;
} else if(string[i] == 'u'){
vowels++;
}
}
return vowels;
}
What am I doing wrong? I'm new to C but have experience in other languages.
Thanks in advance.
Exercising undefined behavior...
You have not allocated any space for the pointer to point at, so when you're trying to use it, the behavior is undefined.
Simply make some space for it:
char buffer[1000];
char* countWord = buffer;
and there's another mistake:
scanf("%s", &countWord);
^
You shouldn't use an address of (&) operator here. Just drop it. You're reading a string into the target of the pointer, not the pointer itself.
Also note that you're doing some mixed code in your function. You're using an uninitialized variable i, yet that seems unnecessary since you're incrementing the pointer string. So you want to drop i and change the if statement to
if (*string == 'a')
And be sure to initialize vowel as well:
int vowel = 0;
From the scanf man page, the format specifier %s:
"Matches a sequence of non-white-space characters; the next pointer must be a pointer to char, and the array must be large enough to accept all the sequence and the terminating NUL character."
The pointer your code provides matching the sole %s specifier, however, is the address of the variable declared as a pointer to char: &countWord is of type pointer to pointer to char. Thus, scanf writes (or attempts to write) the matched sequence to a location sized for a pointer to char, not necessarily the sequence length + null terminator. This may thus write into unallocated memory, which is undefined behavior (and oftentimes, a segfault). Simply removing the address-of operator will not suffice on its own to resolve the issue, either, because simply declaring a pointer to char does not allocate the space for the characters you presumably want said pointer to point to at some point.
What you must do to read a string using scanf as you have attempted to is to ensure that sufficient space is allocated to store the sequence you will read, then pass scanf a pointer to that space. These could be allocated statically:
char countWord[512]; // Assumes input sequence will consist of no more than 511 characters, since space is needed for the terminating NUL character
Or dynamically:
char* countWord = malloc(sizeof(char) * 512); // Same size as the above, so input still must be no more than 511 characters, but dynamically allocated so will need to be explicitly freed later to avoid leaking memory
Given the space is allocated and sufficient, you can then pass countWord (which defined in either of the manners shown above, is effectively a pointer to char, no address-of required*)
If the sequence your scanf call is reading as input is under some defined input restriction, you can pass scanf a pointer to sufficient allocated space for the maximum allowable input sequence size, guaranteeing the read sequence will not exceed the allocated space.
This, however, depends on the input following said limits. Better would be for your code to limit how much it might read, so it really is guaranteed that you won't access unallocated memory, even if your input source decides not to behave as expected. scanf provides a mechanism to do this by including a field width with the specifier, e.g.:
scanf("%511s", countWord); // Reads at most 511 bytes of input into the location pointed to by countWord, plus the NUL terminator.
Obviously, instead of 511 characters of input you would choose a suitable number that all valid inputs to the program should fall within.
The other answer noting that variables should be initialized before being read from is also true, but crashes specifically are more likely to result from interacting with unallocated memory than allocated but merely uninitialized memory (not that reading from the latter isn't undefined behavior).
*Technically there are some differences between a char array declared explicitly as an array and a pointer to char, but those differences are not particularly relevant to this question.
Your lack of memory allocation for countWord was already mentioned in other answers.
But you also have a problem with usage of uninitialized variable while counting the vowels.
You iterate using string pointer and add some random value i on top of that.
int vowelCount(char* string){
int i; // <<=== not initialized, holding ramdon garbage value
for (; *string; string++){
if (string[i] == 'a'){ // << adding random index to pointer.
...
I am practicing allocation memory using malloc() with pointers, but 1 observation about pointers is that, why can strcpy() accept str variable without *:
char *str;
str = (char *) malloc(15);
strcpy(str, "Hello");
printf("String = %s, Address = %u\n", str, str);
But with integers, we need * to give str a value.
int *str;
str = (int *) malloc(15);
*str = 10;
printf("Int = %d, Address = %u\n", *str, str);
it really confuses me why strcpy() accepts str, because in my own understanding, "Hello" will be passed to the memory location of str that will cause some errors.
In C, a string is (by definition) an array of characters. However (whether we realize it all the time or not) we almost always end up accessing arrays using pointers. So, although C does not have a true "string" type, for most practical purposes, the type pointer-to-char (i.e. char *) serves this purpose. Almost any function that accepts or returns a string will actually use a char *. That's why strlen() and strcpy() accept char *. That's why printf %s expects a char *. In all of these cases, what these functions need is a pointer to the first character of the string. (They then read the rest of the string sequentially, stopping when they find the terminating '\0' character.)
In these cases, you don't use an explicit * character. * would extract just the character pointed to (that is, the first character of the string), but you don't want to extract the first character, you want to hand the whole string (that is, a pointer to the whole string) to strcpy so it can do its job.
In your second example, you weren't working with a string at all. (The fact that you used a variable named str confused me for a moment.) You have a pointer to some ints, and you're working with the first int pointed to. Since you're directly accessing one of the things pointed to, that's why you do need the explicit * character.
The * is called indirection or dereference operator.
In your second code,
*str = 10;
assigns the value 10 to the memory address pointed by str. This is one value (i.e., a single variable).
OTOTH, strcpy() copies the whole string all at a time. It accepts two char * parameters, so you don't need the * to dereference to get the value while passing arguments.
You can use the dereference operator, without strcpy(), copying element by element, like
char *str;
str = (char *) malloc(15); //success check TODO
int len = strlen("Hello"); //need string.h header
for (i = 0; i < len; i ++)
*(str+i)= "Hello"[i]; // the * form. as you wanted
str[i] = 0; //null termination
Many string manipulation functions, including strcpy, by convention and design, accept the pointer to the first character of the array, not the pointer to the whole array, even though their values are the same.
This is because their types are different; e.g. a pointer to char[10] has a different type from that of a pointer to char[15], and passing around the pointer to the whole array would be impossible or very clumsy because of this, unless you cast them everywhere or make different functions for different lengths.
For this reason, they have established a convention of passing around a string with the pointer to its first character, not to the whole array, possibly with its length when necessary. Many functions that operate on an array, such as memset, work the same way.
Well, here's what happens in the first snippet :
You are first dynamically allocating 15 bytes of memory, storing this address to the char pointer, which is pointer to a 1-byte sequence of data (a string).
Then you call strcpy(), which iterates over the string and copy characters, byte per byte, into the newly allocated memory space. Each character is a number based on the ASCII table, eg. character a = 97 (take a look at man ascii).
Then you pass this address to printf() which reads from the string, byte per byte, then flush it to your terminal.
In the second snippet, the process is the same, you are still allocating 15 bytes, storing the address in an int * pointer. An int is a 4 byte data type.
When you do *str = 10, you are dereferencing the pointer to store the value 10 at the address pointed by str. Remind what I wrote ahead, you could have done *str = 'a', and this index 0 integer would had the value 97, even if you try to read it as an int. you can event print it if you would.
So why strcpy() can take a int * as parameter? Because it's a memory space where it can write, byte per byte. You can store "Hell" in an int, then "o!" in the next one.
It's just all about usage easiness.
See there is a difference between = operator and the function strcpy.
* is deference operator. When you say *str, it means value at the memory location pointed by str.
Also as a good practice, use this
str = (char *) malloc( sizeof(char)*15 )
It is because the size of a data type might be different on different platforms. Hence use sizeof function to determine its actual size at the run time.
I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char p[5];
char q[]="Hello";
int i=0;
strcpy(p,"Hello");
printf("strlen(p)=%d\n",strlen(p));
printf("sizeof(p)=%d\n",sizeof(p));
printf("strlen(q)=%d\n",strlen(q));
printf("sizeof(q)=%d\n",sizeof(q));
for(i=0;i<6;i++)
{
printf("p[%d]=%c\tq[%d]=%c\n",i,p[i],i,q[i]);
}
return 0;
}
The output that I get is:
strlen(p)=5
sizeof(p)=5
strlen(q)=5
sizeof(q)=6
p[0]=H q[0]=H
p[1]=e q[1]=e
p[2]=l q[2]=l
p[3]=l q[3]=l
p[4]=o q[4]=o
p[5]= q[5]=
I know declaring array like q[]="some string" sets the size of the array equal to the number of characters in the string const, but why is there a difference in the output of sizeof() for both the types of array declaration?
How does the strlen() & the printf() know when to stop, there was no null character added while declaring the two arrays.
There are multiple questions in your question.
strcpy(p,"Hello");
This is illegal since p is only 5 chars long, so there's no room
left for the terminating 0 added by strcpy. Consequently it is
either not 0-terminated or the 0 byte was added outside the available
space - calling strlen on it is also undefined behavior or fishy at
least
Calling sizeof on p is okay and yields the correct value of 5.
Calling strlen(q) yields 5 because q indeed contains a 0 terminator - implicitly added by initializing with a string literal - and there are 5 chars before the 0
Since it contains a 0 terminator, q is really an array of 6
characters so sizeof yields 6.
char p[5];
strcpy(p,"Hello");
copies 5 characters into p and writes the terminating null-character ('\0') at 6th position, i.e. out of the bounds of this array, which yields undefined behavior.
From manual page of strcpy:
"If the destination string of a strcpy() is not large enough, then anything might happen. Any time a program reads or copies data into a buffer, the program first needs to check that there's enough space."
char p[5];
strcpy(p,"Hello");
This strcpy writes a 0 into p[5]. So it's out of bounds. The sizeof(p) is still 5 though.
You have written over the end of p. It's incorrect and results in undefined behavior. In
this case nothing bad happened and it went unnoticed.
The other string you have, has a length of 5 and a sizeof 6.
The q char array also contains the null terminating character. While the fixed size of p doesn't allow the null character to be copied in. Notice that strlen will check for the null character to count the amount of characters of a string, therefore not having one will probably cause undefined behavior.
sizeof(q) is 6, since it contains null terminator.
p does not hold enough space for the null terminator - so strlen(p) can be any random value. This is called undefined behavior.
Strings in C are terminated by a NUL character '\0';
This is why sizeof(q) returns 6, it has enough space to store the '\0' at the end.
You've sized p yourself to be able to hold 5 characters, not enough for the trailing '\0'.
So, this code is undefined behaviour:
strcpy(p, "Hello");
This is copying the '\0' into p[5], which is out-of-bounds.
Question: why is there a difference in the output of sizeof() for both the types of array declaration?
Answer: This statement declares a variable named q, with type char[], pointing at a memory location that holds "Hello".
char q[] = "Hello";
sizeof(q) is 6 because the string "Hello" is comprised of 'H','e','l','l','o','\0', which includes the NULL char in the count.
This statement declares a variable named p, with type char[], pointing to a memory location where 5 char's are reserved.
char p[5];
Note that depending upon memory alignment flags to the compiler, you may actually have 6, 8, or more char's reserved at the location reserved to p. And C won't complain if you reference or assign p[5] (which is the ordinal sixth char in the p[] array).
sizeof(p) is 5 because the compiler has recorded how big the memory location you declared for p. So sizeof(p) and sizeof(q) return different values because p and q are declared differently and refer to different entities.
Question: How does the strlen() & the printf() know when to stop, there was no null character added while declaring the two arrays.
Answer: Both strlen() function calls count the number of non-NULL char's. So both strlen function calls count char's until they locate the NULL terminator. Which both p and q have, at least until the memory location at p+5 is assigned another value. This is because p and q are both allocated on the stack. Look at the addresses of p, q, and the integer i. Here is your function with additional variables added to help illustrate where p and q are located,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)<(b))?(b):(a))
int main()
{
char m0 = 'X';
char p[5];
char m1 = 'Y';
char q[]="Hello";
char m2 = 'Z';
int i=0;
strcpy(p,"World");
printf("strlen(p)=%d\n",strlen(p));
printf("sizeof(p)=%d\n",sizeof(p));
printf("strlen(q)=%d\n",strlen(q));
printf("sizeof(q)=%d\n",sizeof(q));
for(i=0;i<6;i++)
{
printf("p[%d]=%c\tq[%d]=%c\n",i,p[i],i,q[i]);
}
printf("m0=%x, %c\n",&m0,m0);
printf(" p=%x\n",p);
printf("m1=%x, %c\n",&m1,m1);
printf(" q=%x\n",q);
printf("m2=%x, %c\n",&m2,m2);
char *x;
for(x=min(&m0,&m2);x<max(&m0,&m2);x++)
{
printf("x[%x]=%c\n",x,*x);
}
return 0;
}
Observe that m0, m1, and m2 are adjacent to the arrays p[] and q[]. When run on my Linux system, we observe that the strcpy of "World" modifies the value of m0 (replaces the 'X' with '\0').
strlen(p)=5
sizeof(p)=5
strlen(q)=5
sizeof(q)=6
p[0]=W q[0]=H
p[1]=o q[1]=e
p[2]=r q[2]=l
p[3]=l q[3]=l
p[4]=d q[4]=o
p[5]= q[5]=
m0=bfbea6a7,
p=bfbea6a2
m1=bfbea6a1, Y
q=bfbea69b
m2=bfbea69a, Z
x[bfbea69a]=Z
x[bfbea69b]=H
x[bfbea69c]=e
x[bfbea69d]=l
x[bfbea69e]=l
x[bfbea69f]=o
x[bfbea6a0]=
x[bfbea6a1]=Y
x[bfbea6a2]=W
x[bfbea6a3]=o
x[bfbea6a4]=r
x[bfbea6a5]=l
x[bfbea6a6]=d
x[bfbea6a7]=
A C literal string such as "Hello" or "World" is terminated by the NULL char, and includes that char in the size of the string. The strcpy() function copies the entire string, including the NULL char at the end.
You should use strncpy, or check the destination string size. Note that when you used strcpy(p,q), you copied more characters (the NULL terminator) than p[] had allocated. That is something you want to avoid. C does not do boundary checking on arrays, so it will let you perform the strcpy. Though lint would detect this error.
I have never really done much C but am starting to play around with it. I am writing little snippets like the one below to try to understand the usage and behaviour of key constructs/functions in C. The one below I wrote trying to understand the difference between char* string and char string[] and how then lengths of strings work. Furthermore I wanted to see if sprintf could be used to concatenate two strings and set it into a third string.
What I discovered was that the third string I was using to store the concatenation of the other two had to be set with char string[] syntax or the binary would die with SIGSEGV (Address boundary error). Setting it using the array syntax required a size so I initially started by setting it to the combined size of the other two strings. This seemed to let me perform the concatenation well enough.
Out of curiosity, though, I tried expanding the "concatenated" string to be longer than the size I had allocated. Much to my surprise, it still worked and the string size increased and could be printf'd fine.
My question is: Why does this happen, is it invalid or have risks/drawbacks? Furthermore, why is char str3[length3] valid but char str3[7] causes "SIGABRT (Abort)" when sprintf line tries to execute?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main() {
char* str1 = "Sup";
char* str2 = "Dood";
int length1 = strlen(str1);
int length2 = strlen(str2);
int length3 = length1 + length2;
char str3[length3];
//char str3[7];
printf("%s (length %d)\n", str1, length1); // Sup (length 3)
printf("%s (length %d)\n", str2, length2); // Dood (length 4)
printf("total length: %d\n", length3); // total length: 7
printf("str3 length: %d\n", (int)strlen(str3)); // str3 length: 6
sprintf(str3, "%s<-------------------->%s", str1, str2);
printf("%s\n", str3); // Sup<-------------------->Dood
printf("str3 length after sprintf: %d\n", // str3 length after sprintf: 29
(int)strlen(str3));
}
This line is wrong:
char str3[length3];
You're not taking the terminating zero into account. It should be:
char str3[length3+1];
You're also trying to get the length of str3, while it hasn't been set yet.
In addition, this line:
sprintf(str3, "%s<-------------------->%s", str1, str2);
will overflow the buffer you allocated for str3. Make sure you allocate enough space to hold the complete string, including the terminating zero.
void main() {
char* str1 = "Sup"; // a pointer to the statically allocated sequence of characters {'S', 'u', 'p', '\0' }
char* str2 = "Dood"; // a pointer to the statically allocated sequence of characters {'D', 'o', 'o', 'd', '\0' }
int length1 = strlen(str1); // the length of str1 without the terminating \0 == 3
int length2 = strlen(str2); // the length of str2 without the terminating \0 == 4
int length3 = length1 + length2;
char str3[length3]; // declare an array of7 characters, uninitialized
So far so good. Now:
printf("str3 length: %d\n", (int)strlen(str3)); // What is the length of str3? str3 is uninitialized!
C is a primitive language. It doesn't have strings. What it does have is arrays and pointers. A string is a convention, not a datatype. By convention, people agree that "an array of chars is a string, and the string ends at the first null character". All the C string functions follow this convention, but it is a convention. It is simply assumed that you follow it, or the string functions will break.
So str3 is not a 7-character string. It is an array of 7 characters. If you pass it to a function which expects a string, then that function will look for a '\0' to find the end of the string. str3 was never initialized, so it contains random garbage. In your case, apparently, there was a '\0' after the 6th character so strlen returns 6, but that's not guaranteed. If it hadn't been there, then it would have read past the end of the array.
sprintf(str3, "%s<-------------------->%s", str1, str2);
And here it goes wrong again. You are trying to copy the string "Sup<-------------------->Dood\0" into an array of 7 characters. That won't fit. Of course the C function doesn't know this, it just copies past the end of the array. Undefined behavior, and will probably crash.
printf("%s\n", str3); // Sup<-------------------->Dood
And here you try to print the string stored at str3. printf is a string function. It doesn't care (or know) about the size of your array. It is given a string, and, like all other string functions, determines the length of the string by looking for a '\0'.
Instead of trying to learn C by trial and error, I suggest that you go to your local bookshop and buy an "introduction to C programming" book. You'll end up knowing the language a lot better that way.
There is nothing more dangerous than a programmer who half understands C!
What you have to understand is that C doesn't actually have strings, it has character arrays. Moreover, the character arrays don't have associated length information -- instead, string length is determined by iterating over the characters until a null byte is encountered. This implies, that every char array should be at least strlen + 1 characters in length.
C doesn't perform array bounds checking. This means that the functions you call blindly trust you to have allocated enough space for your strings. When that isn't the case, you may end up writing beyond the bounds of the memory you allocated for your string. For a stack allocated char array, you'll overwrite the values of local variables. For heap-allocated char arrays, you may write beyond the memory area of your application. In either case, the best case is you'll error out immediately, and the worst case is that things appear to be working, but actually aren't.
As for the assignment, you can't write something like this:
char *str;
sprintf(str, ...);
and expect it to work -- str is an uninitialized pointer, so the value is "not defined", which in practice means "garbage". Pointers are memory addresses, so an attempt to write to an uninitialized pointer is an attempt to write to a random memory location. Not a good idea. Instead, what you want to do is something like:
char *str = malloc(sizeof(char) * (string length + 1));
which allocates n+1 characters worth of storage and stores the pointer to that storage in str. Of course, to be safe, you should check whether or not malloc returns null. And when you're done, you need to call free(str).
The reason your code works with the array syntax is because the array, being a local variable, is automatically allocated, so there's actually a free slice of memory there. That's (usually) not the case with an uninitialized pointer.
As for the question of how the size of a string can change, once you understand the bit about null bytes, it becomes obvious: all you need to do to change the size of a string is futz with the null byte. For example:
char str[] = "Foo bar";
str[1] = (char)0; // I'd use the character literal, but this editor won't let me
At this point, the length of the string as reported by strlen will be exactly 1. Or:
char str[] = "Foo bar";
str[7] = '!';
after which strlen will probably crash, because it will keep trying to read more bytes from beyond the array boundary. It might encounter a null byte and then stop (and of course, return the wrong string length), or it might crash.
I've written all of one C program, so expect this answer to be inaccurate and incomplete in a number of ways, which will undoubtedly be pointed out in the comments. ;-)
Your str3 is too short - you need to add extra byte for null-terminator and the length of "<-------------------->" string literal.
Out of curiosity, though, I tried
expanding the "concatenated" string to
be longer than the size I had
allocated. Much to my surprise, it
still worked and the string size
increased and could be printf'd fine.
The behaviour is undefined so it may or may not segfault.
strlen returns the length of the string without the trailing NULL byte (\0, 0x00) but when you create a variable to hold the combined strings you need to add that 1 character.
char str3[length3 + 1];
…and you should be all set.
C strings are '\0' terminated and require an extra byte for that, so at least you should do
char str3[length3 + 1]
will do the job.
In sprintf() ypu are writing beyond the space allocated for str3. This may cause any type of undefined behavior (If you are lucky then it will crash). In strlen(), it is just searching for a NULL character from the memory location you specified and it is finding one in 29th location. It can as well be 129 also i.e. it will behave very erratically.
A few important points:
Just because it works doesn't mean it's safe. Going past the end of a buffer is always unsafe, and even if it works on your computer, it may fail under a different OS, different compiler, or even a second run.
I suggest you think of a char array as a container and a string as an object that is stored inside the container. In this case, the container must be 1 character longer than the object it holds, since a "null character" is required to indicate the end of the object. The container is a fixed size, and the object can change size (by moving the null character).
The first null character in the array indicates the end of the string. The remainder of the array is unused.
You can store different things in a char array (such as a sequence of numbers). It just depends on how you use it. But string function such as printf() or strcat() assume that there is a null-terminated string to be found there.