C Struct中的默认值

时间:2009-04-14 20:13:47

标签: c initialization

我有这样的数据结构:

    struct foo {
        int id;
        int route;
        int backup_route;
        int current_route;
    }

和一个名为update()的函数,用于请求对其进行更改。

  update(42, dont_care, dont_care, new_route);

这真的很长,如果我在结构中添加一些东西,我必须在每次调用更新(...)时添加'dont_care'。

我正在考虑将结构传递给它,但事先用'dont_care'填充结构比在函数调用中拼写它更加繁琐。我可以使用默认值dont care在某处创建结构,并在我将其声明为局部变量后设置我关心的字段吗?

    struct foo bar = { .id = 42, .current_route = new_route };
    update(&bar);

将我希望表达的信息传递给更新功能的最优雅的方法是什么?

我希望其他所有内容都默认为-1('不关心'的秘密代码)

10 个答案:

答案 0 :(得分:152)

虽然宏和/或函数(如已经建议的)将起作用(并且可能具有其他积极效果(即调试挂钩)),但它们比需要的更复杂。最简单且可能最优雅的解决方案是仅定义用于变量初始化的常量:

const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something
    dont_care, dont_care, dont_care, dont_care
};
...
struct foo bar = FOO_DONT_CARE;
bar.id = 42;
bar.current_route = new_route;
update(&bar);

这段代码几乎没有理解间接的精神开销,很清楚bar中哪些字段是明确设置的,而(安全地)忽略了那些你未设置的字段。

答案 1 :(得分:15)

您可以将您的秘密特殊值更改为0,并利用C的默认结构成员语义

struct foo bar = { .id = 42, .current_route = new_route };
update(&bar);
然后

将作为初始化程序中未指定的bar成员传递0。

或者您可以创建一个将为您执行默认初始化的宏:

#define FOO_INIT(...) { .id = -1, .current_route = -1, .quux = -1, ## __VA_ARGS__ }

struct foo bar = FOO_INIT( .id = 42, .current_route = new_route );
update(&bar);

答案 2 :(得分:7)

<stdarg.h>允许您定义可变参数函数(它接受无限数量的参数,如printf())。我将定义一个函数,它接受任意数量的参数对,一个指定要更新的属性,另一个指定值。使用enum或字符串指定属性的名称。

答案 3 :(得分:5)

或许可以考虑使用预处理器宏定义:

#define UPDATE_ID(instance, id)  ({ (instance)->id= (id); })
#define UPDATE_ROUTE(instance, route)  ({ (instance)->route = (route); })
#define UPDATE_BACKUP_ROUTE(instance, route)  ({ (instance)->backup_route = (route); })
#define UPDATE_CURRENT_ROUTE(instance, route)  ({ (instance)->current_route = (route); })

如果你的(struct foo)实例是全局的,那么你当然不需要参数。但我假设你可能有不止一个实例。使用({...})块是适用于GCC的GNU-ism;这是一种将线条保持在一起的好方法(安全)。如果您以后需要向宏添加更多内容,例如范围验证检查,则不必担心会破坏if / else语句等等。

根据您指出的要求,我会这样做。像这样的情况是我开始大量使用python的原因之一;处理默认参数,这比使用C更简单。(我猜这是一个python插件,对不起; - )

答案 4 :(得分:4)

如下:

struct foo bar;
update(init_id(42, init_dont_care(&bar)));

使用:

struct foo* init_dont_care(struct foo* bar) {
  bar->id = dont_care;
  bar->route = dont_care;
  bar->backup_route = dont_care;
  bar->current_route = dont_care;
  return bar;
}

struct foo* init_id(int id, struct foo* bar) {
  bar->id = id;
  return bar;
}

并相应地:

struct foo* init_route(int route, struct foo* bar);
struct foo* init_backup_route(int backup_route, struct foo* bar);
struct foo* init_current_route(int current_route, struct foo* bar);

在C ++中,类似的模式有一个我不记得的名字。

编辑:它被称为Named Parameter Idiom

答案 5 :(得分:4)

gobject使用的一种模式是可变参数函数,以及每个属性的枚举值。界面看起来像:

update (ID, 1,
        BACKUP_ROUTE, 4,
        -1); /* -1 terminates the parameter list */

编写varargs函数很简单 - 请参阅http://www.eskimo.com/~scs/cclass/int/sx11b.html。只需匹配键 - &gt;值对并设置适当的结构属性。

答案 6 :(得分:4)

因为看起来你只需要update()函数的这个结构,所以根本不要使用这个结构,它只会模糊你的构造背后的意图。您应该重新考虑更改和更新这些字段的原因,并为这些“小”更改定义单独的函数或宏。

e.g。


#define set_current_route(id, route) update(id, dont_care, dont_care, route)
#define set_route(id, route) update(id, dont_care, route, dont_care)
#define set_backup_route(id, route) update(id, route, dont_care, dont_care)

甚至更好地为每个变更案例编写一个函数。正如您已经注意到的那样,您不会同时更改每个属性,因此可以一次只更改一个属性。这不仅可以提高可读性,还可以帮助您处理不同的情况,例如:您不必检查所有“dont_care”,因为您知道只有当前路线发生了变化。

答案 7 :(得分:2)

我的结构生锈了,所以我可能在这里遗漏了一些关键词。但是为什么不从初始化默认值的全局结构开始,将其复制到本地变量,然后修改它?

初始化程序如:

void init_struct( structType * s )
{
   memcopy(s,&defaultValues,sizeof(structType));
}

然后当你想要使用它时:

structType foo;
init_struct( &foo ); // get defaults
foo.fieldICareAbout = 1; // modify fields
update( &foo ); // pass to function

答案 8 :(得分:2)

您可以使用X-Macro

解决问题

您可以将结构定义更改为:

#define LIST_OF_foo_MEMBERS \
    X(int,id) \
    X(int,route) \
    X(int,backup_route) \
    X(int,current_route)


#define X(type,name) type name;
struct foo {
    LIST_OF_foo_MEMBERS 
};
#undef X

然后您就可以轻松定义一个灵活的函数,将所有字段设置为dont_care

#define X(type,name) in->name = dont_care;    
void setFooToDontCare(struct foo* in) {
    LIST_OF_foo_MEMBERS 
}
#undef X

在讨论here之后,还可以用这种方式定义默认值:

#define X(name) dont_care,
const struct foo foo_DONT_CARE = { LIST_OF_STRUCT_MEMBERS_foo };
#undef X

转化为:

const struct foo foo_DONT_CARE = {dont_care, dont_care, dont_care, dont_care,};

并在hlovdal answer中使用它,其优点是维护更容易,即更改结构成员的数量将自动更新foo_DONT_CARE 。请注意the last "spurious" comma is acceptable

当我必须解决this problem时,我首先学习了X-Macros的概念。

将新字段添加到结构中非常灵活。如果您有不同的数据类型,则可以根据数据类型定义不同的dont_care值:从here,您可以从第二个示例中用于打印值的函数中获取灵感。

如果您对所有int结构都没问题,那么您可以省略LIST_OF_foo_MEMBERS中的数据类型,只需将结构定义的X函数更改为#define X(name) int name;

答案 9 :(得分:1)

最优雅的方法是直接更新结构字段,而不必使用update()函数 - 但可能有充分的理由使用它在问题中没有遇到。

struct foo* bar = get_foo_ptr();
foo_ref.id = 42;
foo_ref.current_route = new_route;

或者你可以像Pukku建议的那样,为结构的每个字段创建单独的访问函数。

否则我能想到的最好的解决方案是将struct字段中的值'0'视为'不要更新'标志 - 所以你只需要创建一个函数来返回一个归零结构,然后用它来更新

struct foo empty_foo(void)
{
    struct foo bar;
    bzero(&bar, sizeof (struct bar));
    return bar;    
}

struct foo bar = empty_foo();
bar.id=42;
bar.current_route = new_route;
update(&bar);

但是,如果0是结构中字段的有效值,则这可能不太可行。