将非空字符串传递给 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 */
注意:这些是您的错误示例,您在整个代码中都做同样的事情。
我正在做 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 */
注意:这些是您的错误示例,您在整个代码中都做同样的事情。