1

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;
    }
  • Well, it's sure hard to offer any kind of answer if you don't show any code. I would be willing to bet that your code is doing something incorrect, and it's nothing to do with the C library. – paddy Dec 07 '21 at 03:35
  • Okay, so please read the output [here](https://godbolt.org/z/W35Tj7s4d) when compiled with `-fsanitize=address`. That should give you a clue. – paddy Dec 07 '21 at 03:43
  • Already pot my code as above. – DerekWangTW Dec 07 '21 at 03:45
  • Already ran it and found the issue, as above-above. – paddy Dec 07 '21 at 03:46
  • thanks for paddy, but why the value is 68, not 678? after I use, it can get 678 for good. memset(kk, 0, sizeof(kk)); // add this strcpy(kk, (endptr + 1));// add this strcpy(buf, kk); – DerekWangTW Dec 07 '21 at 03:57
  • I have posted an answer with a more in-depth explanation, and fixed your program. – paddy Dec 07 '21 at 03:58

1 Answers1

0

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.

paddy
  • 60,864
  • 6
  • 61
  • 103