操纵数组导致段错误?

时间:2013-09-02 02:58:48

标签: c arrays segmentation-fault

我正在编写一个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;

3 个答案:

答案 0 :(得分:3)

以下是此代码可能出现错误的原因:

  1. 节点为NULL,未初始化,指向已释放的内存或
  2. node-&gt; children为NULL,未初始化,指向已释放的内存或
  3. node-&gt; numchildren大于node-&gt; children
  4. 的分配大小

    确保调用代码:

    1. 分配节点
    2. 分配node-&gt; children
    3. 设置node-&gt; numchildren
    4. 传递了节点
    5. 的正确值

      查看粘贴的调用代码...

      调用代码不分配节点;它是一个未初始化的指针,这意味着它指向内存中的随机位置。

      由于命名而误读了该代码。传递下来的孩子是一个未初始化的指针,但这不会在演示的代码中出现段错误,因为它永远不会被解除引用。

      第一次输入{时,它应该可以正常工作。第二次它应该是段错误,因为this = nodethis设置为未初始化的指针。

答案 1 :(得分:1)

您正在错误地初始化节点指针。这意味着您传递的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**,则sizeof内的malloc调用应引用类型T*,依此类推任何数字*个字符。

(3)您的结构定义如下:

struct vsm_node {
    int                   numchildren;
    struct vsm_attribute *attrs   [36];
    struct vsm_node      *children[8];
};

这意味着当您分配struct vsm_node对象时,无论是使用malloc还是仅通过声明struct vsm_node类型的变量,它已经为您的两个数组都分配了足够的内存指针。分配更多内存不仅是不必要的,而且是浪费的。它还可能导致程序的行为与仅使用数组分配额外内存时的行为不同。

答案 2 :(得分:0)

您正在为

中的节点分配内存
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
}