将非空字符串传递给 snprintf 会导致不相关的 char* 数组更改地址

Passing a non-empty string to snprintf causes an unrelated char* array to change addresses

我正在做 K&R 书中的练习,我 运行 在尝试扩展 04-06 以允许具有字符串名称的变量时遇到了一个奇怪的错误。老实说,我实际上已经设法修复了这个错误(非常简单 - 解释如下),但我想首先知道为什么会出现错误。

对于那些不熟悉这个问题的人,基本上要求您创建一个命令行计算器(使用波兰语表示法),它可以存储和调用带有字符名称的变量。

出现问题的相关代码如下:

#define MAXOPLEN 1000

int varCount = 1;
char **keys;
char **values;
// changing the declaration to:
// char strOps[][STROPCOUNT] = { ... };
// fixed the issue
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print",
                             "clr", "sin", "cos", "tan",
                             "exp", "pow", "ln", "log",
                             "mem", "re"};

main() {
    keys = malloc(varCount * sizeof(char[MAXOPLEN]));
    keys[0] = "ans";
    values = malloc(varCount * sizeof(char[MAXOPLEN]));
    values[0] = "0.0";

    ... // Other stuff related to the program
}

// flag is unrelated to the problem I'm asking about. It just checks to see
// if the variable name used to store value n is 'ans', which is where
// the last returned value is stored automatically
void memorize(char s[], double n, bool flag) {
    ...  // small conditional block for flag

    for (i = 0; i < varCount; i++) {
        if (equals(keys[i], s)) {
            found = True;
            // Next line is where the program actually breaks
            snprintf(values[i], MAXOPLEN, "%f", n);
            break;
        }
    }

    if (!found) {
        i = varCount;
        varCount++;

        keys = realloc(keys, varCount * sizeof(char*));
        keys[i] = malloc(sizeof(char[MAXOPLEN]));
        keys[i] = s;

        values = realloc(values, varCount * sizeof(char*));
        values[i] = malloc(sizeof(char[MAXOPLEN]));
        snprintf(values[i], MAXOPLEN, "%f", n);
    }
}

经过编译和运行ning,第一次输入方程式计算,似乎一切都运行顺利。但是,在调试时,我发现 strOps 中的前三个 char* 奇怪地指向了不同的地址。当试图将等式的 return 值保存为 "ans" 时,它会进入 memorize() 中的 for 循环,该循环试图查看字符串 s 是否已被用作键名。它正确地找到 keys[0] 以指向与 s 的值 ("ans") 匹配的字符串,然后尝试将 double n 转换为字符串并将其保存在 values[0].

在 snprintf() 函数内部时,strOps 中的前三个 char* 指向 corecrt_stdio_config.h 中此方法内部的其他位置:

_Check_return_ _Ret_notnull_
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
{
    // Error occurs after this next line:
    static unsigned __int64 _OptionsStorage;
    return &_OptionsStorage;
}

如上面代码中所述,使 strOps 成为二维字符数组(而不是字符指针数组)解决了这个问题。这是有道理的,因为字符数组不能改变单个字符的值,但我不明白的是为什么 corecrt_stdio_config.h 中的那个方法首先改变了这三个指针的值。

谢谢!

您的初始化不正确并导致更改:

keys[0] = "ans";
values[0] = "0.0";

"ans""0.0"都是字符串字面量,不能用于初始化数组,需要在初始化后使用strcpy分配。

strcpy (keys, "ans");
strcpy (values, "0.0");

您的另一种选择是一次分配一个字符:

size_t i;
char *p = "ans";
for (i = 0; i < strlen (p); i++)
    keys[i] = p[i];                 /* copy to keys */
p[i] = 0;                           /* nul-terminate */

注意:这些是您的错误示例,您在整个代码中都做同样的事情。