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));
}
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • 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. – Andrew Henle Jun 21 '20 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. – milanHrabos Jun 21 '20 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. – 0___________ Jun 21 '20 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. – Andrew Henle Jun 21 '20 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. – Andrew Henle Jun 21 '20 at 18:25
  • @AndrewHenle and how many of your "separate states" are going on here: `for(char *src="abcdef", *p=des; (*p++=*src++); printf("%s\n",des));` . Oh, you do not like it? I am sorry for that. For me it looks elegant to pack all information to a loop header and since I am use to see for loop header to contain the most information needed for the body, this one does have all needed. You can see here: https://stackoverflow.com/questions/62502669/for-loop-make-increment-and-accessing-in-one-loop?noredirect=1#comment110535497_62502669, where I am trying to assign value in headerAndNothingWillStopMe – milanHrabos Jun 22 '20 at 21:24
  • @milanHrabos Seriously?!?! "Here's some more 'elegant' code that I wrote that I'm unable to understand. Please help me." You have two questions where you have such a problem figuring out what your code is doing that you have to ask questions here? And you continue to defend writing code that way? I don't care if you want to write code you can't understand even after I've explained to you why you can't understand it - but others at least can learn something. Code that doesn't do what you coded it to do is a mistake, and you are proud that you literally refuse to learn from your mistakes. – Andrew Henle Jun 23 '20 at 05:06

6 Answers6

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;

}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • 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? – milanHrabos Jun 21 '20 at 12:58
  • but they were not. I think it is only from some version of compiler, does not you know what version? – milanHrabos Jun 21 '20 at 12:59
  • 1
    @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. – anastaciu Jun 21 '20 at 13:04
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 );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 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 Jun 21 '20 at 13:27
  • 1
    @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. – RobertS supports Monica Cellio Jun 21 '20 at 16:27
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);
anastaciu
  • 23,467
  • 7
  • 28
  • 53
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
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.

gavinb
  • 19,278
  • 3
  • 45
  • 60
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"));
}
0___________
  • 60,014
  • 4
  • 34
  • 74
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.

  • 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 Jun 21 '20 at 18:01
  • 1
    @anastaciu In life crazy things happen. I sometimes see accepted answers to old questions that do not even address the question. – RobertS supports Monica Cellio Jun 21 '20 at 18:12
  • 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 Jun 21 '20 at 18:21
  • 1
    @anastaciu One example: [Here](https://stackoverflow.com/questions/93039/where-are-static-variables-stored-in-c-and-c#comment6070653_93411) - This is the direct link to a comment regarding the concern the answer is misplaced. – RobertS supports Monica Cellio Jun 21 '20 at 18:30
  • 1
    @anastaciu The answer actually addressed the question title, but not the question. – RobertS supports Monica Cellio Jun 21 '20 at 18:41
  • Yep, you're right, the below answer has a broken link, should we remove it? – anastaciu Jun 21 '20 at 19:09