首页 文章

C Struct中的默认值

提问于
浏览
88

我有这样的数据结构:

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 回答

  • 4

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

  • 2

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

    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 中您明确设置了哪些字段(安全地)忽略了那些未设置的字段 .

  • 7

    您可以将您的秘密特殊值更改为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

    也许考虑使用预处理器宏定义:

    #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的原因之一;处理默认参数等变得比以往任何时候都简单得多 . (我猜这是一个python插件,对不起;-)

  • 4

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

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

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

  • 4

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

    例如

    #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”,因为您知道只有当前路线发生了变化 .

  • 1

    怎么样的:

    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中,类似的模式有一个我不记得的名字 .

    EDIT :它被称为Named Parameter Idiom .

  • 15

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

    初始化程序如:

    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
    
  • 151

    你可以用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 answerwith the advantage that here maintenance is easier, i.e. changing the number of struct members will automatically update 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;

  • 5

    最优雅的方法是直接更新结构字段,而不必使用 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是结构中字段的有效值,则这可能不太可行 .

相关问题