Search code examples
gccwindows-subsystem-for-linuxstrcpystrtol

Why this situation was happen? c lib problem while using Windows WSL(ubuntu 20.04)


I meet one strange problem while using gcc for c lib in strcpy and strtol function. Test on two situation and get the very different results.

//#The bad code is "res=68"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char buf[10];
    
    char* endptr;
    int x;
    int res;

    memset(buf, 0, sizeof(buf));
    res=0;

    strcpy(buf, "a678b");
    while (*(buf) != '\0') {
        x = strtol(buf, &endptr, 10);
        if (x == 0) {
             
            strcpy(buf, (endptr + 1));
            
        }
        else {
            strcpy(buf, endptr);
        }
        res+= x;
    }
    printf("%d", res);
    return 0;
}

After change to the following area, it can get the right value: 678. But, why?

while (*(buf) != '\0') {
        x = strtol(buf, &endptr, 10);
        if (x == 0) {
            memset(kk, 0, sizeof(kk)); // add this  
            strcpy(kk, (endptr + 1));// add this
            strcpy(buf, kk);
        }
        else {
            strcpy(buf, endptr);
        }
        res+= x;
    }

Solution

  • Calls to strcpy, memcpy and friends are not allowed to use memory that overlaps. If the memory overlaps, the behavior is undefined. For short copies like you're doing, this will very likely produce strange results due to optimizations that copy multiple bytes at a time.

    There is a function that is designed for overlapping memory, called memmove.

    However, you shouldn't need to use these functions at all. I have updated your program below to achieve this by simply walking a pointer through your string. There seems to be no need to keep copying it back to the beginning of buf.

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char buf[10] = "a678b";
        int res = 0;
    
        for (const char *s = buf; *s != '\0'; )
        {
            char* endptr;
            int x = strtol(s, &endptr, 10);
            if (s == endptr)
              ++endptr;
            else
              res += x;
            s = endptr;
        }
    
        printf("%d", res);
    }
    

    This program produces the output:

    678
    

    Note also I changed the test for when strtol doesn't read a number. The return value 0 can be perfectly valid. The proper way to test if it actually parsed a number is to check if endptr was advanced past the beginning of the string.

    I also initialized res to 0, because in your program you did not initialize it before adding values to it. That is also undefined behavior.

    So to summarize, you had a few cases of undefined behavior in your program due to misuse of standard library functions and not initializing memory.