3

According to this: strcpy vs strdup, strcpy could be implemented with a loop, they used this while(*ptr2++ = *ptr1++). I have tried to do similar:

#include <stdio.h> #include <stdlib.h> int main(){ char *des = malloc(10); for(char *src="abcdef\0";(*des++ = *src++);); printf("%s\n",des); } 

But that prints nothing, and no error. What went wrong?

Thanks a lot for answers, I have played a bit, and decided how best to design the loop to see how the copying is proceeding byte by byte. This seems the best:

#include <stdio.h> #include <stdlib.h> int main(){ char *des = malloc(7); for(char *src="abcdef", *p=des; (*p++=*src++); printf("%s\n",des)); } 
7
  • 3
    Learn something from this: needlessly cramming all those operations into a single line of code made it impossible for you to understand what was actually happening. There's NOTHING gained from seeing how much code you can cram into a single line. Commented Jun 21, 2020 at 13:17
  • @AndrewHenle so you once see a usage of for loop in an uncommon way and called it cramming? Well for loop is pretty basic concept among all programming, and c has the advantage to use it whatever way you like. I use it a lot in different way that for(int i =0;i<10;i++) like you are used to, but that is just my preference. Commented Jun 21, 2020 at 13:22
  • 1
    @milanHrabos as a very beginner you have a bit too strong opinions. Your code is hard to read and experienced programmers prefer to write more lines instead of "compressing" in into this kind of monsters. Commented Jun 21, 2020 at 13:30
  • 1
    @milanHrabos You had to ask this very question because that one line of code was too complex for you to figure out. End of story. "Brevity of code" is cargo-cult BS that causes the very issues that prevented you from being able to understand what your code was doing. There is a whole body of peer-reviewed research that demonstrates that a human mind can only keep track of a handful of separate states simultaneously - that number usually varies from 3 to 6 or 7 things. The (*des++ = *src++) part of your for loop has 6 things going on at once. Yeah - you crammed too much there. Commented Jun 21, 2020 at 18:22
  • 2
    You can learn from this, or you can keep banging your head against brick walls trying to make sense of code that has too much crammed into one line. Your choice. Commented Jun 21, 2020 at 18:25

6 Answers 6

3

In this loop

for(char *src="abcdef\0";(*des++ = *src++);); 

the destination pointer des is being changed. So after the loop it does not point to the beginning of the copied string.

Pay attention to that the explicit terminating zero character '\0' is redundant in the string literal.

The loop can look the following way

for ( char *src = "abcdef", *p = des; (*p++ = *src++);); 

And then after the loop

puts( des ); 

and

free( des ); 

You could write a separate function similar to strcpy the following way

char * my_strcpy( char *des, const char *src ) { for ( char *p = des; ( *p++ = *src++ ); ); return des; } 

And call it like

puts( my_strcpy( des, "abcdef" ) )' free( des ); 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, good answer, although there is no need to free to pointer at the end of the program, since that is the job of OS
@milanHrabos Well, he means it well, if your example would be part of a bigger program. In fact, there is no real-world program what will just copy a string to dynamic memory and print it. Vlad considered that and made it very good considered part of the answer.
3

You are incrementing des so naturally at the end of the cycle it will be pointing past the end of the string, printing it amounts to undefined behavior, you have to bring it back to the beginning of des.

#include <stdio.h> #include <stdlib.h> int main(){ int count = 0; char *des = malloc(10); if(des == NULL){ return EXIT_FAILURE; //or otherwise handle the error } // '\0' is already added by the compiler so you don't need to do it yourself for(char *src="abcdef";(*des++ = *src++);){ count++; //count the number of increments } des -= count + 1; //bring it back to the beginning printf("%s\n",des); free(dest); //to free the allocated memory when you're done with it return EXIT_SUCCESS; } 

Or make a pointer to the beginning of des and print that instead.

#include <stdio.h> #include <stdlib.h> int main(){ char *des = malloc(10); if(des == NULL){ return EXIT_FAILURE; //or otherwise handle the error } char *ptr = des; for(char *src="abcdef";(*des++ = *src++);){} //using {} instead of ;, it's clearer printf("%s\n",ptr); free(ptr) // or free(dest); to free the allocated memory when you're done with it return EXIT_SUCCESS; } 

3 Comments

here (*des++ = *src++), does have to be satisfy, but it is only satisfy - as long as - the value (address) is NOT 0. But if your did not put the \0 at the end of *src, then it will continue (read) infinity (or to crash). But is does not crash, so does it compiler for your? Does he put the '\0' null byte for you, at the end of the string?
but they were not. I think it is only from some version of compiler, does not you know what version?
@milanHrabos, yes src is null terminated by the compiler i.e. it puts a '\0' at the end of the string, all compilers do this, it's mandatory.
2

printf("%s\n",des); is undefined behavior (UB) as it attempts to print starting beyond the end of the string written to allocated memory.

Copy the string

Save the original pointer, check it and free when done.

const char *src = "abcdef\0"; // string literal here has 2 ending `\0`, char *dest = malloc(strlen(src) + 1); // 7 char *d = dest; while (*d++ = *src++); printf("%s\n", dest); free(dest); 

Copy the string literal

const char src[] = "abcdef\0"; // string literal here has 2 ending `\0`, char *dest = malloc(sizeof src); // 8 for (size_t i = 0; i<sizeof src; i++) { dest[i] = src[i]; } printf("%s\n", dest); free(dest); 

3 Comments

You could use while (*d++ = *p++); in the second example too if you would assign the address of the first element of the array to a pointer p.
@RobertSsupportsMonicaCellio in the "Copy the string literal", while (*d++ = *p++); stops 1 too soon as the literal has 2 null characters.
Yes, but OP wants the string, not another redundant null character. I like pedantry but to transport his issue is not necessary.
1

You just need to remember the original allocated pointer.

Do not program in main. Use functions.

 #include <stdio.h> #include <stdlib.h> size_t strSpaceNeedeed(const char *str) { const char *wrk = str; while(*wrk++); return wrk - str; } char *mystrdup(const char *str) { char *wrk; char *dest = malloc(strSpaceNeedeed(str)); if(dest) { for(wrk = dest; *wrk++ = *str++;); } return dest; } int main(){ printf("%s\n", mystrdup("asdfgfd")); } 

or even better

size_t strSpaceNeedeed(const char *str) { const char *wrk = str; while(*wrk++); return wrk - str; } char *mystrcpy(char *dest, const char *src) { char *wrk = dest; while((*wrk++ = *src++)) ; return dest; } char *mystrdup(const char *str) { char *wrk; char *dest = malloc(strSpaceNeedeed(str)); if(dest) { mystrcpy(dest, str); } return dest; } int main(){ printf("%s\n", mystrdup("asdfgfd")); } 

Comments

1

You allocate the destination buffer des and correctly copy the source string into place. But since you are incrementing des for each character you copy, you have moved des from the start of the string to the end. When you go to print the result, you are printing the last byte which is the nil termination, which is empty.

Instead, you need to keep a pointer to the start of the string, as well as having a pointer to each character you copy.

The smallest change from your original source is:

#include <stdio.h> #include <stdlib.h> int main(){ char *des = malloc(10); char *p = des; for(char *src="abcdef";(*p++ = *src++);); printf("%s\n",des); } 

So p is the pointer to the next destination character, and moves along the string. But the final string that you print is des, from the start of the allocation.

Of course, you should also allocate strlen(src)+1 worth of bytes for des. And it is not necessary to null-terminate a string literal, since that will be done for you by the compiler.

7 Comments

Is it possible to assign 3 vars? liek char *p = *des = malloc(10)?
"The smallest change from your original source is" - The smallest change is not always the best one.
Also you are saying that des would point to the NUL character which is wrong. It points one element past NUL.
"Of course, you should also allocate strlen(src)+1 worth of bytes for des." - While this brings a memory advantage, it is not necessary to do so. 10 chars will hold the string too. Also OP would need to place char *src="abcdef" before char *des = malloc(10); that way. You forgot to mention that.
@milanHrabos "Is it possible to assign 3 vars? like char *p = *des = malloc(10)?" - Yes, but not in this way. F.e. char* des, *p; p = des = malloc(sizeof(char) * 10);
|
1

But that prints nothing, and no error. What went wrong?

des does not point to the start of the string anymore after doing (*des++ = *src++). In fact, des is pointing to one element past the NUL character, which terminates the string, thereafter.

Thus, if you want to print the string by using printf("%s\n",des) it invokes undefined behavior.

You need to store the address value of the "start" pointer (pointing at the first char object of the allocated memory chunk) into a temporary "holder" pointer. There are various ways possible.

#include <stdio.h> #include <stdlib.h> int main (void) { char *des = malloc(sizeof(char) * 10); if (!des) { fputs("Error at allocation!", stderr); return 1; } char *tmp = des; for (const char *src = "abcdef"; (*des++ = *src++) ; ); des = temp; printf("%s\n",des); free(des); } 

Alternatives:

#include <stdio.h> #include <stdlib.h> int main (void) { char *des = malloc(sizeof(char) * 10); if (!des) { fputs("Error at allocation!", stderr); return 1; } char *tmp = des; for (const char *src = "abcdef"; (*des++ = *src++) ; ); printf("%s\n", tmp); free(tmp); } 

or

#include <stdio.h> #include <stdlib.h> int main (void) { char *des = malloc(sizeof(char) * 10); if (!des) { fputs("Error at allocation!", stderr); return 1; } char *tmp = des; for (const char *src = "abcdef"; (*tmp++ = *src++) ; ); printf("%s\n", des); free(des); } 

Side notes:

  • "abcdef\0" - The explicit \0 is not needed. It is appended automatically during translation. Use "abcdef".

  • Always check the return of memory-management function if the allocation succeeded by checking the returned for a null pointer.

  • Qualify pointers to string literal by const to avoid unintentional write attempts.

  • Use sizeof(char) * 10 instead of plain 10 in the call the malloc. This ensures the write size if the type changes.

  • int main (void) instead of int main (void). The first one is standard-compliant, the second not.

  • Always free() dynamically allocated memory, since you no longer need the allocated memory. In the example above it would be redundant, but if your program becomes larger and the example is part-focused you should free() the unneeded memory immediately.

6 Comments

The OP did change the accepted answer, at least, to a better answer. Unfortunately, not to mine :/, well, we can't have it all.
@anastaciu In life crazy things happen. I sometimes see accepted answers to old questions that do not even address the question.
Yes, that makes sense, the way it is, basically the OP can accept whatever, even plain wrong answers, itt's the nature of it. We can only rely on responsible users downvoting it, but that can always fail if no one is paying atention.
@anastaciu One example: Here - This is the direct link to a comment regarding the concern the answer is misplaced.
@anastaciu The answer actually addressed the question title, but not the question.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.