为什么此 C 结构初始化代码会产生总线错误?

Why does this C struct initialization code produce a bus error?

在用 C 语言设计游戏实体系统时,我尝试了一种 "equals-free" 初始化方法。我很惊讶地看到 linter 告诉我在我的 init 函数末尾存在内存泄漏,并且我的变量 ent 从未在以下代码中初始化。结果是对的,因为我得到了 "bus error":

#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 指针作为参数提供,告诉它指向一些新分配的内存,并且然后填写那个记忆,一切都会好起来的。我不知道导致 "bus error" 的真正原因是什么,我是否遗漏了一些关于指针和 malloc 的关键信息?

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

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 失败信号的方法来自 entity_init

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_init() 函数内进行分配,则需要 return 指向分配的指针,或者通过使 ent 指向指针的指针来添加一个间接层至 entity_t。在发布的代码中,entity_init() ent 中是传递给函数的指针的 copy。对该指针所做的任何更改,例如将内存分配的地址分配给该指针,对于调用函数都是不可见的,因为该副本将在函数 returns.

之后不复存在。

另请注意,您需要检查从 malloc() 编辑的值 return 以确保分配成功。如果成功,该函数可以继续初始化过程;如果不是,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