我正在编写一个C应用程序,它涉及将一个文本文件(以名为VSM的格式,因此下面的名称)解析为一个树结构。格式设计器称它为标记树。每个节点都有一些键值对(或属性)和子节点。
以下是有关的结构和功能:
vsm.h:
struct vsm_node {
int numchildren;
struct vsm_attribute *attrs [36];
struct vsm_node *children[8];
};
void vsm_addchild(struct vsm_node *node, struct vsm_node *child);vsm.c:
#include "vsm.h"
void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
node->children[node->numchildren] = child;
++(node->numchildren);
}为什么当我调用vsm_addchild时,它会产生分段故障?
对不起,如果这是一个愚蠢的问题,但我真的很烂的普通C,特别是如果它涉及指针和内存管理。
编辑以包含进行调用的代码:
#include <stdio.h>
#include "vsm.h"
void vsm_parse(struct vsm_node *tree, FILE *fp, char *name)
{
struct vsm_node *this = tree;
int ch;
while ((ch = fgetc(fp)) != 0) {
...
else if (ch == '{') {
struct vsm_node *node;
vsm_initnode(node);
vsm_addchild(this, node);
this = node;
...
...编辑:添加了vsm_initnode函数,但我不知道我是否做得对。
void vsm_initnode(struct vsm_node *node)
{
node = malloc( sizeof(struct vsm_node *));
node->attrs = malloc(36 * sizeof(struct vsm_attribute *));
node->children = malloc( 8 * sizeof(struct vsm_node *));
node->numchildren = 0;
int i;
for (i = 0; i < 36; ++i)
node->attrs[i] = NULL; /* unnecessary? */
for (i = 0; i < 8; ++i)
node->children[i] = NULL;
}
void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
node->children[node->numchildren] = child;
++(node->numchildren);
}gdb产出:
Program received signal SIGSEGV, Segmentation fault.
vsm_addchild (node=0x28, child=0x7541612d <msvcrt!_atodbl_l+2294>) at vsm.c:62
62 node->children[node->numchildren] = child;发布于 2013-09-02 04:14:16
您正在错误地初始化节点指针。这意味着您正在传递的node变量无效,并随机地指向内存,几乎可以肯定地指向进程不拥有的内存中,从而导致分段错误。试一试:
// Change (1): new return type, parameter removed
struct vsm_node* vsm_initnode()
{
struct vsm_node* node;
node = malloc( sizeof(struct vsm_node)); // Change (2)
// The following lines are unnecessary - change (3)
//node->attrs = malloc(36 * sizeof(struct vsm_attribute *));
// node->children = malloc( 8 * sizeof(struct vsm_node *));
node->numchildren = 0;
int i;
for (i = 0; i < 36; ++i)
node->attrs[i] = NULL; // unnecessary but good practice
for (i = 0; i < 8; ++i)
node->children[i] = NULL;
return node;
}
void vsm_addchild(struct vsm_node *node, struct vsm_node *child)
{
node->children[node->numchildren] = child;
++(node->numchildren);
}对每个错误的解释:
(1) -最初是将struct vsm_node*作为参数传递给函数。这允许您修改指针引用的任何值。但是,虽然您可以修改指针本身(就像调用malloc那样),但这些更改没有反映在调用者中。您所有的内存分配和初始化都被浪费了。
(2) - struct vsm_node*类型的指针必须指向足够大的内存,以容纳struct vsm_node。相反,您以前分配了足够的内存来容纳struct vsm_node*,而这个内存还不够大。一个好的经验法则是,malloc调用的右侧应该包含比左侧少一个级别的间接。也就是说,如果要分配给的变量具有T**类型,那么malloc内部的sizeof调用应该引用T*类型,等等,对于任意数量的*字符。
(3)您的结构定义如下:
struct vsm_node {
int numchildren;
struct vsm_attribute *attrs [36];
struct vsm_node *children[8];
};这意味着,当您分配一个struct vsm_node对象时,无论是通过使用malloc还是简单地声明一个struct vsm_node类型的变量,它都已经有足够的内存来处理两个指针数组。分配更多内存不仅是不必要的,而且是浪费的。它还可能导致您的程序行为不同,如果您只是使用数组分配额外的内存。
发布于 2013-09-02 04:16:09
中的节点分配内存。
vsm_initnode(struct vsm_node *node) 在这里,节点是一个局部变量,当vsm_initnode()完成其执行并返回到其调用模块时,您为节点所做的分配将丢失。某种程度上是悬空指针发出。为了保留分配,您必须使用双指针或返回内存地址。
在下面,您可以同时找到两个实现(双指针和返回内存地址)。
void vsm_parse(struct vsm_node *tree, FILE *fp, char *name)
{
struct vsm_node *this = tree;
int ch;
while ((ch = fgetc(fp)) != 0) {
...
else if (ch == '{') {
// Creating a object pointer.
struct vsm_node *node;
/**********************************/
/* Double Pointer Implementation */
/**********************************/
vsm_initnode_double_pointer(&node);
/******************************************/
/* Return Memory Address Implementation */
/******************************************/
node=vsm_initnode_return_address();
vsm_addchild(this, node);
this = node;
...
...
// Double Pointer Implementation
void vsm_initnode_double_pointer(struct vsm_node **node)
{
*node=malloc(sizeof(struct vsm_node *));
// No need to allocate memory for attrs, children because it has been declared as array. You can directly store the memory location values in it.
(*node)->numchildren = 0;
int i;
for (i = 0; i < 36; ++i)
(*node)->attrs[i] = NULL;
for (i = 0; i < 8; ++i)
(*node)->children[i] = NULL;
}
// Return Memory Address Implementation
struct vsm_node * vsm_initnode_return_address()
{
struct vsm_node *temp=malloc(sizeof(struct vsm_node *));
// No need to allocate memory for attrs, children because it has been declared as array. You can directly store the memory location values in it.
temp->numchildren = 0;
int i;
for (i = 0; i < 36; ++i)
temp->attrs[i] = NULL;
for (i = 0; i < 8; ++i)
temp->children[i] = NULL;
return temp
}https://stackoverflow.com/questions/18565113
复制相似问题