How does this example from K and R work [duplicate] - c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does “while(*s++ = *t++)” work?
I was trying to understand the following example. I am a little confused how this would actually work.
void strcpy(char *s, char *t)
{
while (*s++ = *t++)
;
}
Any help is great. Thanks!

Remember that a string in C is just a pointer to a list of chars, terminated with a \0.
Also remember that \0 (the null byte) is falsy, that is, if it's in a condition, that condition will be false.
This function gets a pointer to the start of the source string and one to the start of the destination string.
It then loops over each character in the source string, copying the character to the destination string. When the condition is evaluated, the post-increment ++ will advance the pointer forward a byte.
This implementation also has an issue, as far as I can tell. If the source string isn't the exact same length, it won't have a null terminator at the end. For safety's sake, you should tack a \0 at the end of the destination string.

The value of *s++ = *t++ is the value of the right side of the assignment, *t. So the loop will terminate when *t is 0, i.e., at the end of the string pointed by t. The condition also increments the value of t (and s), after assigning the character pointed by t to the char pointed by s. There is nothing in the loop body, the condition by itself does the copy.

Related

Post increment with pointers in while loop in C

I am writing the strcat function
/*appends source string to destination string*/
#include <stdio.h>
int main()
{
char srcstr[100], deststr[100];
char *psrcstr, *pdeststr;
printf("\n Enter source string: ");
gets(srcstr);
printf("\n Enter destination string: ");
gets(deststr);
pdeststr = deststr;
psrcstr = srcstr;
while(*pdeststr++)
;
while(*pdeststr++ = *psrcstr++)
;
printf("%s", deststr);
return 0;
}
For srcstr = " world" and deststr = "hello" I get hello, when I expect to see hello world, which is what I see if I change the first while so
while(*pdeststr);
pdeststr++;
why can't I write all in one line in the first while, just like in the second while?
Your one line loop
while(*pdeststr++);
Is equivalent to
while(*pdeststr)
pdeststr++;
pdeststr++;
Because the postincrement operator is executed before the condition is tested, but after the value for the test is determined.
So you could cater for this with
while(*pdeststr++);
pdeststr--;
Mandatory introduction: do not use gets(), use fgets()!.
Your problem is here:
while(*pdeststr++)
;
The side effect of incrementing is carried out in your last iteration step (when pdeststr points to the NUL terminator), so after this loop, pdeststr points one after your NUL terminator. Write it like this instead:
while(*pdeststr) ++pdeststr;
The boolean value for the while condition is computed before the ++ post-increment.
So when your while loop exits, the post-increment operator is executed one last time, hence pdeststr is pointing right after the null terminator char that follows the word "hello".
Then the rest of the program appends more data after that null char. You end up with the string "hello\0world\0". The print function thinks the string ends at the first null char it encounters.
You have an extra incrementation that point you after the NULL char, and so finally you could print only the first string.
By precedence the postfix increment operator (ptr++) is higher than the indirection (dereference) operator (*ptr). Therefore the
while(*pdeststr++);
will always increment the pdeststr first then evaluate the previously pointed value. As an outcome, when the result of evaluation is 0 the pdeststr actually points to the next element, so there will be a null-terminator character ('\0') between your concatenated words.
As a one-liner solution with while loop you can use the short-circuit evaluation as follows:
while(*pdeststr && pdeststr++);
The code snippet above will stop when *foo results 0 and won't evaluate the foo++ part.
My conclusion is that at the end, deststr = "hello\0 world\0" and because of that printf("%s", deststr); find the first \0 and gives as output hello

*char ending with double '\0'

My code is crashing because of a lack of the char '\0' at the end of some strings.
It's pretty clear to me why we have to use this termination char. My question is,
is there a problem adding a potential 2nd null character to a character array - to solve string problems?
I think it's cheaper just add a '\0' to every string than verify if it needs and then add it, but I don't know if it's a good thing to do.
is there a problem to have this char ('\0') twice at the end of a string?
This question lacks clarity as "string" means different things to people.
Let us use the C specification definition as this is a C post.
A string is a contiguous sequence of characters terminated by and including the first null character. C11 §7.1.1 1
So a string, cannot have 2 null characters as the string ends upon reaching its first one. #Michael Walz
Instead, re-parse to "is there a problem adding a potential 2nd null character to a character array - to solve string problems?"
A problem with attempting to add a null character to a string is confusion. The str...() functions work with C strings as defined above.
// If str1 was not a string, strcpy(str1, anything) would be undefined behavior.
strcpy(str1, "\0"); // no change to str1
char str2[] = "abc";
str2[strlen(str2)] = '\0'; // OK but only, re-assigns the \0 to a \0
// attempt to add another \0
str2[strlen(str2)+1] = '\0'; // Bad: assigning outside `str2[]` as the array is too small
char str3[10] = "abc";
str3[strlen(str3)+1] = '\0'; // OK, in this case
puts(str3); // Adding that \0 served no purpose
As many have commented, adding a spare '\0' is not directly attending the code's fundamental problem. #Haris #Malcolm McLean
That unposted code is the real issue that need solving #Yunnosch, and not by attempting to append a second '\0'.
I think it's cheaper just add a '\0' to every string than verify if it needs and then add it, but I don't know if it's a good thing to do.
Where would you add it? Let's assume we've done something like this:
char *p = malloc(32);
Now, if we know the allocated length, we could put a '\0' as the last character of the allocated area, as in p[31] = '\0'. But we don't how long the contents of the string are supposed to be. If there's supposed to be just foobar, then there'd still be 25 bytes of garbage, which might cause other issues if processed or printed.
Let alone the fact that if all you have is the pointer to the string, it's hard to know the length of the allocated area.
Probably better to fix the places where you build the strings to do it correctly.
Having '\0' is not a problem, unless you have not gone out of bounds of that char array.
You do have to understand that, having '\0' twice would mean, any string operation would not even know that there is a second '\0'. They will just read till the first '\0', and be with it. For them, the first '\0' is the Null terminating character and there should not be anything after that.

String Concatenation in C with Pointers

So this is the standard string concatenation code in C:
char *stringcat(char *dest, const char *src){
char *save=dest;
while(*save !='\0'){
save++;
}
while(*src!='\0'){
*save=*src;
save++;
src++;
}
*save='\0';
return dest;
}
My question is why when we replace the first while loop with the following:
while(*save++){};
It does not work, but, when replaced with:
while(*++save){};
It does work. In the first two instances, save points to the null terminator at the end of dest at the end, which is then overwritten by the first character in src. However, in the third instance, it seems like save will be pointing to the character after the null terminator, which is weird.
If you make it while (*save++) {}, the operation you are repeating is: load byte, increment pointer, check whether byte was zero. Therefore, what will happen at the end of the string is: load null byte, increment pointer, check whether byte was zero, see it was, exit loop. So save will be pointing just after the null byte. But you want to start copying the second string on top of that null byte, not after it.
(Is it possible that you have the meanings of ++save and save++ interchanged in your head? If so, here's a useful mnemonic: ++save means increment, then load the value; save++ means load the value, then increment. So the order in which the variable name and the ++ appear corresponds to the order of operations.)
while(*save++)
First dereference save and compare that value to 0, then increment save.
while(*save !='\0'){
save++;
}
First dereference save, compare to 0 and only increment if non-zero.
See the difference?

Why adding a Null Character in a String array? [duplicate]

This question already has answers here:
Why are strings in C++ usually terminated with '\0'?
(5 answers)
Why do we need to add a '\0' (null) at the end of a character array in C?
(9 answers)
Closed 9 years ago.
I know that we have to use a null character to terminate a string array like this:
char str[5] = { 'A','N','S','\0' };
But I just wanted to know why is it essential to use a null character to terminate an array like this?
Also why don't we add a null charater to terminate these :-
char str1[5]="ANS";
The NULL-termination is what differentiates a char array from a string (a NULL-terminated char-array) in C. Most string-manipulating functions relies on NULL to know when the string is finished (and its job is done), and won't work with simple char-array (eg. they'll keep on working past the boundaries of the array, and continue until it finds a NULL somewhere in memory - often corrupting memory as it goes).
In C, 0 (the integer value) is considered boolean FALSE - all other values are considered TRUE. if, for and while uses 0 (FALSE) or non-zero (TRUE) to determent how to branch or if to loop. char is an integer type, an the NULL-character (\0) is actually and simply a char with the decimal integer value 0 - ie. FALSE. This make it very simple to make functions for things like manipulating or copying strings, as they can safely loop as long as the character it's working on is non-zero (ie. TRUE) and stop when it encounters the NULL-character (ie. FALSE) - as this signifies the end of the string. It makes very simple loops, since we don't have to compare, we just need to know if it's 0 (FALSE) or not (TRUE).
Example:
char source[]="Test"; // Actually: T e s t \0 ('\0' is the NULL-character)
char dest[8];
int i=0;
char curr;
do {
curr = source[i];
dest[i] = curr;
i++;
} while(curr); //Will loop as long as condition is TRUE, ie. non-zero, all chars but NULL.
It isnt essential but if you are using any of the standard libraries, they all expect it.

String concatenation when trying to copy arrays in C

I'm confused as to what is causing this behavior in my program. I'm just trying to copy the contents of one char* array to another and instead of copying the element, but it's concatenating the strings in a strange way. I'm doing something like this:
char* a[50];
char* b[50];
for(int n=0; n<x; n++){
a[n] = malloc(sizeof(char) * (1 + strlen(b[n])));
strcpy(a[n], b[n]);
}
Has anyone experienced this before? I can post my output if that helps.
Thanks.
A few issues:
You loop while n < x. What is x? If x >= 50, you'll run off the end of your arrays. Your loop condition needs to protect against this possibility.
Also, remember that the memory returned by malloc is uninitialized. It is good that you are allocating an extra byte for the NULL terminator. What you are missing is the code that actually sets the value of the NULL terminator. The consequence of this is that your a[] strings are most likely not NULL-terminated (the last character is whatever random garbage that was previously stored in that byte). When you try to print them out or use a string function like strlen on it, you'll read past the end of the string and into whatever happens to be sitting in the memory range that follows.
Trying using strncpy, note that you have add the null termination character at the end of a[n], This way you are sure to know what you are copying rather relying on the assumption that b[n] has null terminating character.

Resources