【问题标题】:Why does this C struct initialization code produce a bus error? [duplicate]为什么这个 C 结构初始化代码会产生总线错误? [复制]
【发布时间】:2018-05-13 18:21:43
【问题描述】:

在用 C 语言设计游戏实体系统时,我尝试了一种“equals-free”初始化方法。我很惊讶地看到一个 linter 告诉我在我的 init 函数结束时存在内存泄漏,并且我的变量 ent 从未在以下代码中初始化。结果证明是对的,因为我遇到了“总线错误”:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int x;
    int y;
} entity_t;

void entity_init(entity_t* ent, int _x, int _y)
{
    ent = malloc(sizeof(*ent));
    ent->x = _x;
    ent->y = _y;
}

int main(void)
{
    entity_t* ent;
    entity_init(ent, 10, 24);
    printf("Entity: x%d y%d", ent->x, ent->y);
    return 0;
}

认为上面的代码会做的是将我的空ent 指针作为参数提供,告诉它指向一些新分配的内存,然后填充该内存和所有内容会好的。我不知道导致“总线错误”的真正原因是什么,我是否遗漏了一些关于指针和 malloc 的关键信息?

模糊地记得之前在一些 C 代码中看到过与此非常相似的东西(equals-free 结构初始化),我强烈希望使用类似于此的 equals-free 初始化样式(损坏) 代码 if 这样的事情在 C 中是可能的。

【问题讨论】:

  • ent = malloc(sizeof(*ent)); 覆盖传递的函数参数。结束。剩下的就是main 中的内存泄漏和未定义的行为。疯狂 - entity_t* ent; 甚至还没有被初始化为 NULL,所以它甚至可能在不中断的情况下做一些奇怪的事情。
  • @WeatherVane 再次...
  • 更改时,在子函数中,调用函数中指针指向的地方,子函数的参数必须是指针的地址。在当前场景下,调用应该是:entity_init( &amp;ent, 10, 24);,签名应该是:void entity_init(entity_t** ent, int _x, int _y)
  • 在编写变量、参数和宏名称时,不要使用前导下划线。前导下划线,尤其是后跟大写字母时,是为操作系统“保留”的。
  • 关于:ent = malloc(sizeof(*ent)); 始终检查 (!=NULL) 返回值以确保操作成功。

标签: c pointers struct malloc bus-error


【解决方案1】:

如果您必须在entity_init() 函数中进行分配,您需要返回一个指向分配的指针,或者通过使ent 成为指向entity_t 的指针来添加一个间接层。在发布的代码中,entity_init() ent 内是传递给函数的指针的副本。对该指针所做的任何更改,例如将内存分配的地址分配给该指针,都不会从调用函数中看到,因为该副本将在函数返回后不复存在。

另外,请注意,您需要检查从malloc() 返回的值,以确保分配成功。如果成功,函数可以继续初始化过程;如果不是,ent 可以保持为空指针,应在调用函数中检查:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int x;
    int y;
} entity_t;

void entity_init(entity_t **ent, int _x, int _y)
{
    *ent = malloc(sizeof **ent);
    if (*ent) {
        (*ent)->x = _x;
        (*ent)->y = _y;
    }
}

int main(void)
{
    entity_t *ent;
    entity_init(&ent, 10, 24);

    if (ent == NULL) {
        fprintf(stderr, "Allocation failure in entity_init()\n");
        exit(EXIT_FAILURE);
    }

    printf("Entity: x%d y%d\n", ent->x, ent->y);

    return 0;
}

程序输出:

Entity: x10 y24

【讨论】:

    【解决方案2】:

    malloc调用移到初始化函数之外:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct {
        int x;
        int y;
    } entity_t;
    
    void entity_init(entity_t* ent, int _x, int _y)
    {
        ent->x = _x;
        ent->y = _y;
    }
    
    int main(void)
    {
        entity_t* ent;
        if(NULL==(ent = malloc(sizeof(*ent))))
            return 1;
        entity_init(ent, 10, 24);
        printf("Entity: x%d y%d", ent->x, ent->y);
        return 0;
    }
    

    您正在将指向已分配块的指针分配给局部变量 (ent)。这不会影响main 中的ent

    如果您想将malloc 保留在entity_init 中,则应使用双指针,但您还应更改签名以允许从malloc 发出malloc 失败的信号

    int entity_init(entity_t **ent, int _x, int _y)
    {
        if(NULL==(*ent = malloc(sizeof(**ent))))
            return -1;
        (*ent)->x = _x;
        (*ent)->y = _y;
    }
    
    int main(void)
    {
        entity_t* ent;
        if(0>entity_init(&ent, 10, 24))
            return 1;
        printf("Entity: x%d y%d", ent->x, ent->y);
        return 0;
    }
    

    更常见的模式是:

    entity_t *entity_new(int _x, int _y)
    {
        entity_t *ent = malloc(sizeof(*ent));
        if (NULL==ent) 
            return NULL;
        ent->x = _x;
        ent->y = _y;
        return ent;
    }
    
    int main(void)
    {
        entity_t* ent;
        if(NULL==(ent=entity_new(10,24)))
            return 1;
        printf("Entity: x%d y%d", ent->x, ent->y);
        return 0;
    }
    

    【讨论】:

    • ...或返回它:'entity_t* entity_init(int _x, int _y)'
    • @MartinJames 将其合并到答案的扩展版本中:)
    • 为什么 ent 永远是 NULL,在 entity_new 函数中,在最后一个例子中?
    • @Accumulator malloc 可能会失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    • 2016-02-08
    • 2020-10-11
    • 2012-10-24
    • 2012-07-28
    • 1970-01-01
    相关资源
    最近更新 更多