尝试使用 strtok 时内存损坏

Memory corruption when attempting to use strtok

当我在我的微控制器上 运行 这段代码时,它在尝试在“price_right_of_period”上打印时崩溃。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DEBUG

char *str = ".45";

int main()
{
    char *decimal_pos; // where the decimal place is in the string
    char buffer[64]; // temporary working buffer
    
    int half_price_auto_calc = 1;
    
    // the cost of the product price difference for half price mode
    int price_save_left, price_save_right = 0;
    int price_half_left, price_half_right = 0;

    // EG: let's say we get the string ".89"
    char *price_left_of_period; // this will contain "2"
    char *price_right_of_period; // this will contain "89"
    
    // find where the decimal is
    decimal_pos = strstr(str, ".");
    
    if (decimal_pos != NULL)
    {
        printf("\nThe decimal point was found at array index %d\n", decimal_pos - str);
        printf("Splitting the string \"%s\" into left and right segments\n\n", str);
    }
    
    // get everything before the period
    strcpy(buffer, str); // copy the string
    
    price_left_of_period = strtok(buffer, ".");
    
    // if the dollar sign exists, skip over it
    if (price_left_of_period[0] == '$') price_left_of_period++;
    
    #ifdef DEBUG
        printf("price_left_of_period = \"%s\"\n", price_left_of_period);
    #endif
    
    // get everything after the period
    //
    // strtok remembers the last string it worked with and where it ended
    // to get the next string, call it again with NULL as the first argument
    price_right_of_period = strtok(NULL, "");
    
    #ifdef DEBUG
        printf("price_right_of_period = \"%s\"\n\n", price_right_of_period);
    #endif
    
    if (half_price_auto_calc == 1)
    {
        // calculate the amount we saved (before the decimal)
        price_save_left = atoi((const char *)price_left_of_period);

        // halve the value if half price value is true
        price_half_left = price_save_left / 2;
        
        // calculate the amount we saved (before the decimal)
        price_save_right = atoi((const char *)price_right_of_period);

        // halve the value if half price value is true
        price_half_right = price_save_right / 2;
        
        #ifdef DEBUG
            printf("price_half_left = \"%d\"\n", price_half_left);
            printf("price_half_right = \"%d\"", price_half_right);
        #endif
    }

    return 0;
}

代码 运行 在这里工作正常:https://onlinegdb.com/kDAw2cJyz。然而,如上所述,在我的 MCU 上它崩溃了(下图)。

有人知道为什么我的代码会导致这种情况发生吗?代码对我来说看起来不错,但从其他 C 专家那里得到第二意见总是很好 :)

解决方案:

Your code does have one bug. %d\n", decimal_pos - str doesn't work because decimal_pos - str has the wrong type to print through %d. You need to cast it (I doubt that's causing the crash but you can test by commenting it out and re-testing)

这里的代码确实有bug:

printf("\nThe decimal point was found at array index %d\n", decimal_pos - str);

2 个指针的差异具有类型 ptrdiff_t,可能与 %d 预期的 int 不同。您应该使用 %td 或将差异转换为 (int)(decimal_pos - str)。然而,令人惊讶的是,这种类型不匹配是导致您出现问题的原因。

请注意,您复制字符串时未在 strcpy(buffer, str); 中测试其长度,这对于此示例是可以的,但如果 str 指向更长的字符串,则可能会出现未定义的行为。

代码太复杂:不需要 strtok(),因为您已经有了小数点的偏移量(如果有的话)。您可以将 atoi() 与指向整数部分开头的指针一起使用,而无需使用空字节修补 .。您也可以使用 strtol() 来避免 strstr()

另请注意,在大多数情况下,代码会计算出错误的价格:".45" 将更改为 ".22",这大大超过了 50% 的回扣。

您应该将该数字转换为美分的整数并使用它来计算降价。

这是一个简化版本:

#include <stdio.h>
#include <stdlib.h>

int half_price_auto_calc = 1;
char *str = ".45";

int main() {
    int cents;
    char *p = str;
    if (*p == '$')
        p++;
    // use strtol to convert dollars and cents
    cents = strtol(p, &p, 10) * 100;
    if (*p == '.') {
        cents += strtol(p + 1, NULL, 10);
    }
    if (half_price_auto_calc) {
        cents /= 2;
    }
    printf("reduced price: $%d.%02d\n", cents / 100, cents % 100);
    return 0;
}