c - 如何修复由 realloc 越界引起的段错误?
问题描述
您好,TIA 为您提供帮助。由于我不熟悉发布问题,因此我欢迎任何有关如何提出此问题的反馈。我在 SO 中进行了很多研究,但没有找到我认为我在寻找的东西。
我还在努力,我不太擅长 C。
我的目的是从给定 XML 的某些特定标签中提取数据并将其写入文件。我的问题出现了,因为当我尝试填充为此目的创建的数据结构时,函数在某个时刻realloc()
给了我一个指向超出范围的地址的指针。
如果你看这个例子
#include <stdio.h>
int main() {
char **arrayString = NULL;
char *testString;
testString = malloc(sizeof("1234567890123456789012345678901234567890123456789"));
strcpy(testString, "1234567890123456789012345678901234567890123456789");
int numElem = 0;
while (numElem < 50) {
numElem++;
arrayString = realloc(arrayString, numElem * sizeof(char**));
arrayString[numElem-1] = malloc(strlen(testString)+1);
strcpy(arrayString[numElem-1], testString);
}
printf("done\n");
return 0;
}
它对我的代码做了类似但简化的事情。基本上尝试用 c 字符串填充 char** 但它会出现段错误。(是的,我知道我使用的是 strcpy 而不是它更安全的替代方案,但据我所知,它会复制到 '\0',当你在“”之间写一个字符串时,它会自动包含在内,这就是我所需要的)
我将在下面深入解释。
在这段代码中,我使用了 libxml2,但您不需要知道它来帮助我。
我有一个这样声明的自定义结构:
struct List {
char key[24][15];
char **value[15];
int size[15];
};
struct List *list; //i've tried to make this static after reading that it could make a difference but to no avail
其中充满了必要的键值。list->size[]
用零初始化,以跟踪我插入了多少个值value
。
value
之所以这样描述,是因为对于每个键,我需要一个 char* 数组来存储与其关联的每个值。(我已经考虑过了,但这可能是一种错误的方法,欢迎提出建议——但这不是问题的目的)
我遍历 xml 文件,并且对于每个节点,我strcmp
在节点名称和我的每个键之间执行一个操作。当有匹配时,该键的索引被用作value
矩阵中的索引。然后我尝试为 c 字符串矩阵扩展分配的内存,然后为单个 char* 扩展分配的内存。
“损坏的”代码如下,其中
read
是上述key的索引。reader
是 xmlNodestring
包含 xmlNode 的名称,但随后被释放,因此将其视为新的 char*list
是上面声明的结构
if (xmlTextReaderNodeType(reader) == 3 && read >= 0)
{
/* pull out the node value */
xmlChar *value;
value = xmlTextReaderValue(reader);
if (value != NULL) {
free(string);
string=strdup(value);
/*increment array size */
list->size[read]++;
/* allocate char** */ list->value[read]=realloc(list->value[read],list->size[read] * sizeof(char**));
if (list->value[read] == NULL)
return 16;
/*allocate string (char*) memory */
list->value[read][list->size[read]-1] = realloc(list->value[read][list->size[read]-1], sizeof(char*)*sizeof(string));
if (list->value[read][list->size[read]-1] == NULL)
return 16;
/*write string in list */
strcpy(list->value[read][list->size[read]-1], string);
}
/*free memory*/
xmlFree(value);
}
xmlFree(name);
free(string);
我希望这会分配 char**,然后是 char*,但是在这段代码的几次迭代之后(这是一个包含在 while 循环中的函数),我得到了一个段错误。
用 gdb 分析这个(不是它的专家,只是动态地学习它)我注意到确实代码似乎在 15 次迭代中按预期工作。在第 16 次迭代时,list->value[read][list->size[read]-1]
大小增加后,list->value[read][list->size[read]-1]
指向一个 0x51,标记为地址越界。realloc 仅将其带到 0x3730006c6d782e31,仍标记为越界。我希望它指向最后分配的值。
这是一张图片:https ://imgur.com/a/FAHoidp
如何在不超出范围的情况下正确分配所需的内存?
解决方案
您的代码有很多问题:
- 您没有包括所有适当的标题。你是怎么得到这个编译的?如果您使用
malloc
andrealloc
,则需要#include <stdlib.h>
. 如果您使用strlen
andstrcpy
,则需要#include <string.h>
. - 这并不是一个错误,但除非您应用
sizeof
类型本身,否则您不必使用括号。 - 停止使用
sizeof str
来获取字符串的长度。正确和安全的方法是strlen(str)+1
。如果sizeof
有一天你申请一个指针,你会遇到麻烦。 - 不要用作,或
sizeof(type)
的参数。相反,使用. 这将避免您的错误,而是将其替换为正确转换为. 不过,这一次,至少在 GCC 上,纯属巧合拯救了你。malloc
calloc
realloc
sizeof *ptr
numElem * sizeof(char**)
numElem * sizeof *arrayString
numElem * sizeof(char*)
sizeof(char**) == sizeof(char*)
- 如果您正在动态分配内存,则还必须在不再需要时手动解除分配。用于
free
此目的:free(testString);
,free(arrayString);
。 - 这不是一个错误,但如果你想循环遍历元素,请使用
for
循环,而不是while
循环。这样,每个读者都知道您的意图。
这段代码在 GCC 上编译得很好:
#include <stdio.h> //NULL, printf
#include <stdlib.h> //malloc, realloc, free
#include <string.h> //strlen, strcpy
int main()
{
char** arrayString = NULL;
char* testString;
testString = malloc(strlen("1234567890123456789012345678901234567890123456789") + 1);
strcpy(testString, "1234567890123456789012345678901234567890123456789");
for (int numElem = 1; numElem < 50; numElem++)
{
arrayString = realloc(arrayString, numElem * sizeof *arrayString);
arrayString[numElem - 1] = malloc(strlen(testString) + 1);
strcpy(arrayString[numElem - 1], testString);
}
free(arrayString);
free(testString);
printf("done\n");
return 0;
}
推荐阅读
- docker - 来自 docker 容器的 ssh
- css - CSS:如何在跨度或 div 中垂直对齐文本
- javascript - 将基于类的组件转换为基于函数的组件
- python - Django ValidationError: [""('1',)" value 必须是 True、False 或 None。"]
- python - 具有元组键的字典的平均值
- mongodb - mongodb - 将 $addField 与 $exists 结合起来
- node.js - AWS节点js lambda使用gm imagemagick调整大小,裁剪和单色超时
- istio - 在 istio 中,主体名称是如何创建的?
- c++ - 单例类构造函数的问题在此上下文中是私有的
- powershell - 在 Azuredevops 发布管道中使用 powershell 脚本执行 SSIS 作业