【问题标题】:Memory corruption when attempting to use strtok尝试使用 strtok 时内存损坏
【发布时间】:2021-12-31 22:10:04
【问题描述】:

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

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

#define DEBUG

char *str = "$3.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 "$2.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 专家那里获得第二意见总是很高兴:)

解决方案:

您的代码确实有一个错误。 %d\n", decimal_pos - str 不起作用,因为 decimal_pos - str 的类型错误 通过 %d 打印。你需要投射它(我怀疑这会导致 崩溃,但您可以通过将其注释掉并重新测试来测试)

【问题讨论】:

  • price_right_of_period = strtok(NULL, ""); - 您打算使用哪些字符作为标记分隔符?
  • @BobJarvis-ReinstateMonica 他只打算使用隐式标记分隔符——字符串的结尾。
  • @torxe:它会记住要删除的字符串的其余部分。这没有说明分隔符。或者就此而言,之前的令牌。
  • 我想知道如果没有指定额外的分隔符,您的微控制器的 C 库是否存在错误和崩溃。您可以通过在第二次调用strtok 时将"." 作为分隔符列表进行测试吗?
  • 顺便说一句,您的代码确实有一个错误。 ... %d\n", decimal_pos - str) 不起作用,因为 decimal_pos - str 的类型错误,无法通过 %d 打印。你需要投射它。 (我怀疑这是导致崩溃的原因,但您可以通过将其注释掉并重新测试来进行测试。)

标签: c strtok


【解决方案1】:

这里的代码确实有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()

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

您应该将该数字转换为整数美分,然后用它来计算降价。

这是一个简化版:

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

int half_price_auto_calc = 1;
char *str = "$3.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;
}

【讨论】:

  • 谢谢!我如何将cents 转换为指针。 char *price_left_of_periodsprintf(price_left_of_period, "%d", cents / 100);itoa(cents / 100, price_left_of_period, 10); 也许?原因是我稍后在程序中有一些需要指针的函数。
  • 您应该将目标数组作为一对参数(char *dest, size_t size) 传递,并使用snprintf(dest, size, "$%d.%02d", cents / 100, cents % 100); 构造价格
  • Arduino C 库没有名为snprintf 的函数。
  • 太糟糕了,改用 sprintf 并删除 size 参数
  • 啊,谢谢。那解决了它。我想我不能按照我的意图直接使用指针,因为在 sprintf 中使用它之前它没有初始化,我必须使用数组。
猜你喜欢
  • 2012-06-13
  • 2012-07-05
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-03
相关资源
最近更新 更多