C 操纵数组会导致segfault?

C 操纵数组会导致segfault?,c,arrays,segmentation-fault,C,Arrays,Segmentation Fault,我正在编写一个C应用程序,它涉及到将一个文本文件(以一种称为VSM的格式,因此使用下面的名称)解析为一个树结构。格式的设计者称之为标记树。每个节点都有一些键值对(或属性)和子节点 以下是所讨论的结构和功能: vsm.h: struct vsm_node { int numchildren; struct vsm_attribute *attrs [36]; struct vsm_node *children[8]; };

我正在编写一个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\u addchild
时会产生一个segfault

抱歉,如果这是一个愚蠢的问题,但我真的很烂的普通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;
            ...
        ...
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;
程序接收信号SIGSEGV,分段故障。
vsm.c:62处的vsm_addchild(节点=0x28,子节点=0x7541612d)
62节点->子节点[节点->子节点]=子节点;

以下是此代码可能出现故障的原因:

  • 节点为空、未初始化、指向释放的内存或
  • 节点->子节点为空、未初始化、指向已释放内存或
  • 节点->子节点大于节点->子节点的分配大小
  • 确保呼叫代码:

  • 分配节点
  • 分配节点->子节点
  • 设置节点->numchildren
  • 为节点传入正确的值

  • 查看粘贴的调用代码

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

    由于命名错误,误读了代码。传递的子对象是未初始化的指针,但在演示的代码中不会出现segfault,因为它从未被解除引用


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

    以下是此代码可能segfault的原因:

  • 节点为空、未初始化、指向释放的内存或
  • 节点->子节点为空、未初始化、指向已释放内存或
  • 节点->子节点大于节点->子节点的分配大小
  • 确保呼叫代码:

  • 分配节点
  • 分配节点->子节点
  • 设置节点->numchildren
  • 为节点传入正确的值

  • 查看粘贴的调用代码

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

    由于命名错误,误读了该代码。传递的子项是未初始化的指针,但这不会在演示的代码中显示segfault,因为它从未被取消引用


    第一次
    {
    输入后,它应该可以正常工作。第二次它应该会出错,因为
    this=node
    this
    设置为未初始化的指针。

    您错误地初始化了节点指针。这意味着您传递的
    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);
    }
    

    这意味着,当您分配
    struct vsm_节点
    对象时,无论是通过使用
    malloc
    还是通过简单地声明
    struct vsm_节点
    类型的变量,它都已经有足够的内存用于两个指针数组。分配更多内存不仅是不必要的,而且是浪费的。它还可能导致您的程序无法正常运行ave与仅使用数组并分配额外内存时的情况不同。

    您错误地初始化了节点指针。这意味着您传递的
    节点
    变量无效,并随机指向内存,几乎可以肯定指向您的进程不拥有的内存,从而导致分段错误出现故障。请尝试以下操作:

    // 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);
    }
    

    这意味着,当您分配
    struct vsm_节点
    对象时,无论是通过使用
    malloc
    还是通过简单地声明
    struct vsm_节点
    类型的变量,它都已经有足够的内存用于两个指针数组。分配更多内存不仅是不必要的,而且是浪费的。它还可能导致您的程序无法正常运行ave与仅使用阵列并分配额外内存的情况不同。

    您正在为中的节点分配内存

    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
    }
    
    void vsm_parse(结构vsm_节点*树,文件*fp,字符*name)
    {
    结构vsm_节点*此=树;
    int-ch;
    而((ch=fgetc(fp))!=0){
    ...
    else如果(ch='{'){
    //创建对象指针。
    结构vsm_节点*节点;
    /**********************************/
    /*双指针实现*/
    /**********************************/
    vsm_initnode_双_指针(&node);
    /******************************************/
    /*返回存储器地址实现*/
    /******************************************/
    node=vsm_initnode_return_address();
    vsm_addchild(此,节点);
    这个=节点;
    ...
    ...
    //双指针实现
    void vsm_initnode_双_指针(结构vsm_节点**节点)
    {
    *node=malloc(sizeof(struct vsm_node*);
    //无需为属性、子级分配内存,因为它已声明为数组。您可以直接在其中存储内存位置值。
    (*节点)->numchildren=0;
    int i;
    对于(i=0;i<36;++i)
    (*节点)->attrs[i]=NULL;
    对于(i=0;i<
    
    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
    }