结构初始化的C结构

时间:2018-02-23 19:04:19

标签: c struct reference initialization typedef

我对初始化复杂结构的最佳方法有点困惑 (结构的结构)

我基本上有这样的设置

typedef struct {
    float x;
    float y;
} Vectorf;

typedef struct {
    Vectorf position;
    Vectorf direction;
    Vectorf velocity;
} Player;

初始化Player对象的最佳方法是什么?

变体A

Vectorf position = {1.0f, 1.0f};
Vectorf direction = {1.0f, 0.0f};
Vectorf velocity = {0.0f, 0.0f};

Player player = {position, direction, velocity};

变体B

Player *player = malloc(sizeof(Player));

player->position.x = 1.0f;
player->position.y = 1.0f;
player->direction.x = 1.0f;
player->direction.y = 0.0f;
player->velocity.x = 0.0f;
player->velocity.y = 0.0f;

--- Stuff ---

free(player);

甚至可以创建像

这样的功能
Player *createPlayer(float px, float py...)
Player createPlayer(float px, float py...)

但在这些内部我会再次需要变体A或B。

这只是一种品味问题还是有益处?

我也有类似的东西

typedef struct {
    int x;
    int y;
    Texture *texture;
    bool walkable;
} Tile;

typedef struct {
    int width;
    int height;
    Tile *tiles;
} Map;

这里的创建函数似乎更合理,因为我可以传递实际的地图数据。

3 个答案:

答案 0 :(得分:2)

Player player = {.position = {1.0f, 1.0f}, 
             .direction = {1.0f, 0.0f},
             .velocity = {0.0f, 0.0f}};

注意这在C ++中不起作用。仅限于C。

答案 1 :(得分:2)

我认为答案应该取决于您的需求,两种创作方式 对象没问题。

如果您使用malloc,有时您无需担心分配内存 将只使用一个对象几次,你不需要 当函数退出时,对象“幸存”。所以你的变种A就可以了。

有时你想创建很多对象并将它们存储在其他数据结构(列表,树等)中, 并且你需要在整个程序中“生活”对象。在这种情况下你的 变体B会更好。 AnT的答案向您展示了如何保存线条 用于初始化的代码。

我考虑使用malloc的另一次是 当我知道结构非常大并且会占用大量字节时 每个对象,因此有很多可能会占用你所有的堆栈。在 那种情况我宁愿把这些大对象放在堆里,也要处理 使用指针比在调用时创建副本要便宜得多 不带指针的函数。

当我将结构体视为类并且我想要一个干净的API时,我也使用malloc 创造,使用和destryoing他们。我总是使用前缀来表示函数和我 总是有每个对象类型的create,init和free函数 像这样:

typedef struct abc {
    // lot's of members
} Abc;

Abc *abc_create(void);
int abc_init(Abc *abc); // always with default values
void abc_free(Abc *abc);

int abc_do_A(Abc *abc, int x);
int abc_do_B(Abc *abc, int y);
....

并且前三个函数通常如下所示:

Abc *abc_create(void)
{
    Abc *abc = calloc(1, sizeof *abc);
    if(abc == NULL)
        return NULL;

    if(abc_init(abc) == 1)
        return abc;

    free(abc);
    return NULL;
}

int abc_init(Abc *abc)
{
    if(abc == NULL)
        return 0;

    // initializations
    ...

    return 1;
}

void abc_free(Abc *abc)
{
    if(abc == NULL)
        return;

    // do free of other malloced
    // members if present
    free(abc);
}

我认为这为您提供了一个非常简单易用的API。我维护一个C库 在工作中我至少有50多个结构,都采用相同的方案, 使用它们很容易,因为它们都表现得像这样:

Abc *abc = abc_create(1, 2, 3);
if(abc == NULL)
{
    error handling
}

abc_do_A(abc, 12);
abc_do_A(abc, 11);
...
abc_free(abc); // when not needed anymore

答案 2 :(得分:0)

对于变体A

Player player = { { 1., 1. }, { 1., 0. }, { 0., 0. } };

您也可以使用带标签的变体来提高清晰度和可读性,例如@ dev_null的答案。

对于变体B 复合文字将帮助您

Player *player = malloc(sizeof *player);

*player = (Player) { { 1., 1. }, { 1., 0. }, { 0., 0. } };

如果您拥有类似memdup的功能,则可以将Variant B实现为单行

/* Assuming `void *memdup(const void *src, size_t size);` */

Player *player = memdup(
  &(Player) { { 1., 1. }, { 1., 0. }, { 0., 0. } },
  sizeof *player);

如果您更喜欢,请随时使用已标记的版本。

复合文字允许您混合搭配原始方法和基于{}的方法,如果您出于某种原因决定它是有益的

Player *player = malloc(sizeof *player);

player->position = (Vectorf) { 1., 1. };
player->direction = (Vectorf) { 1., 0. }; 
player->velocity = (Vectorf) { 0., 0. };