【问题标题】:how to assign multiple values into a struct at once?如何一次将多个值分配给一个结构?
【发布时间】:2012-03-05 09:41:55
【问题描述】:

我可以在初始化 struct Foo 时这样做:

Foo foo =  {bunch, of, things, initialized};

但是,我不能这样做:

Foo foo;
foo = {bunch, of, things, initialized};

那么,两个问题:

  1. 为什么我不能做后者,前者是只用于初始化的特殊构造函数吗?
  2. 我怎样才能做类似于第二个例子的事情,即在一个结构已经初始化之后,在一行代码中声明一堆变量?我试图避免对具有许多变量的大型结构执行此操作:

    Foo foo;
    
    foo.a = 1;
    foo.b = 2;
    foo.c = 3;
    //... ad infinitum
    

【问题讨论】:

  • "前者是只用于初始化的特殊构造函数吗?" 没错,就是所谓的聚合初始化
  • C 还是 C++?选择一个。
  • C(++) 在某些领域仍然非常原始。
  • @jim :它们是不同的语言,因此需要提出不同的问题。对于这个问题,请选择一种语言。
  • @jim 在 C 和 C++ 中寻求解决方案使其成为两个不同的问题,尽管它们具有共同的主题。您一次只能问一个问题。我给了 C 的答案,但是当我意识到这个问题有两个标签时,我删除了它。

标签: c++ c struct


【解决方案1】:

如果您关心效率,您可以定义一个与您的结构长度相同的联合,并且您可以立即分配一个类型。

要按元素分配值,请使用联合的结构,要分配整个数据,请使用联合的其他类型。

typedef union
{
    struct
    {
      char a;
      char b;
    } Foo;
    unsigned int whole;
} MyUnion;

MyUnion _Union;
_Union.Foo.a = 0x23;    // assign by element
_Union.Foo.b = 0x45;    // assign by element
_Union.whole = 0x6789;  // assign at once

注意你的记忆组织(“a”是“whole”的 MSB 还是 LSB?)。

【讨论】:

    【解决方案2】:

    在 C++11 中,您可以使用“tie”(在元组标题中声明)执行多重赋值

    struct foo {
        int a, b, c;
    } f;
    
    std::tie(f.a, f.b, f.c) = std::make_tuple(1, 2, 3);
    

    如果你的右手表达式是固定大小的,你只需要获取部分元素,你可以使用带 tie 的忽略占位符

    std::tie(std::ignore, f.b, std::ignore) = some_tuple; // only f.b modified
    

    如果您发现语法 std::tie(f.a, f.b, f.c) 代码过于混乱,您可以使用返回该引用元组的成员函数

    struct foo {
        int a, b, c;
        auto members() -> decltype(std::tie(a, b, c)) {
            return std::tie(a, b, c);
        }
    } f;
    
    f.members() = std::make_tuple(1, 2, 3);
    

    所有这些当然都假设重载赋值运算符不是一个选项,因为你的结构不能由这样的值序列构造,在这种情况下你可以说

    f = foo(1, 2, 3);
    

    【讨论】:

    • 表演怎么样?做这样的事情有开销吗?还是在编译时解决?
    【解决方案3】:

    内存占用 - 这是一个有趣的 i386 补充。

    经过很多麻烦,使用 优化memcpy 似乎使用 i386 与 GCC 和 C99 生成的占用空间最小。我在这里使用 -O3 。 stdlib 似乎手头有各种有趣的编译器优化,这个例子利用了它(memcpy 实际上是在这里编译出来的)。

    这样做:

    Foo foo; //some global variable
    
    void setStructVal (void)   {
    
        const Foo FOO_ASSIGN_VAL = {    //this goes into .rodata
                .bunch       = 1,
                .of          = 2,
                .things      = 3,
                .initialized = 4
        };
    
        memcpy((void*) &FOO_ASSIGN_VAL, (void*) foo, sizeof(Foo));
    
        return;
    }
    

    结果:

    • (.rodata) FOO_ASSIGN_VAL 存储在 .rodata 中
    • (.text) 出现一系列 *movl FOO_ASSIGN_VAL, %registers*
    • (.text) 一系列 movl %registers, foo 发生

    示例:

    • 说 Foo 是一个 48 个字段的 uint8_t 值结构。它在内存中对齐。

    • (IDEAL) 在 32 位机器上,这个 可以 与 12 条立即数的 MOVL 指令一样快到 foo 的地址空间。对我来说,这是 12*10 == 120 字节的 .text 大小。

    • (ACTUAL) 但是,使用 AUTO 的答案可能会在 .text 中生成 48 条 MOVB 指令。对我来说,这是 48*7 == 336 字节的 .text!!

    • (SMALLEST*) 使用上面的 memcpy 版本。 IF对齐被照顾,

      • FOO_ASSIGN_VAL 放置在 .rodata(48 字节)中,
      • 12 MOVL 进入 %register
      • 12 个 MOVL outof %registers 用于 .text (24*10) == 240bytes。
      • 对我来说,这总共是 288 个字节。

    所以,至少对于我的 i386 代码而言,

    - Ideal:    120 bytes
    - Direct:   336 bytes
    - Smallest: 288 bytes
    

    *这里最小的意思是“我所知道的最小的足迹”。它的执行速度也比上述方法更快(24 条指令对 48 条)。当然,IDEAL 版本是最快和最小的,但我还是想不通。

    -贾斯汀

    *有人知道如何实现上面的“IDEAL”吗?真是烦死我了!!

    【讨论】:

      【解决方案4】:

      如果你不太关心效率,你可以双重赋值:即使用聚合初始化创建一个结构的新实例,然后将其复制过来:

      结构 Foo foo; { struct Foo __tmp__ = {bunch, of, things, initialized}; foo = __tmp__; }

      确保将部分包裹在 {} 中,以便在不再需要时立即丢弃不必要的临时变量。

      请注意,这不如在结构中(如果是 c++)或在结构外创建一个“set”函数来接受结构指针(如果是 C)那么有效。但是,如果您需要一个快速的、最好是临时的替代方法来编写逐个元素的赋值,这可能会做到。

      【讨论】:

      • 编译器也会优化它。
      • 为什么要停在那里? struct Foo foo_new(int bunch, int of, int things, int initialized) { struct Foo tmp = {bunch, of, things, initialized}; return tmp; }; struct Foo foo; foo = foo_new(1, 2, 3, 4); 将其作为inline 粘贴在头文件中,优化后对性能的影响应该最小。唯一的缺点是您不能遗漏任何要在最后归零的东西(尽管鉴于missing-field-initializers 警告的存在,有些人可能不认为这是一个缺点)。当然,(Foo){...} 样式可能是最简单的。
      【解决方案5】:

      第一个是聚合初始值设定项 - 您可以在此解决方案中阅读这些和标记的初始值设定项:

      What is tagged structure initialization syntax?

      这是一种特殊的初始化语法,你不能在你的结构初始化之后做类似的事情。您可以做的是提供一个成员(或非成员)函数,将您的一系列值作为参数,然后在成员函数中分配 - 这将允许您在以同样的方式初始化结构后完成此操作简洁(当然是在你第一次编写函数之后!)

      【讨论】:

      • 我发现这个帖子问了同样的问题:stackoverflow.com/questions/1705147/…。然而,所有的答案,连同戴维斯(我真的很喜欢和赞成),似乎都是编译器特定的。这是我可以在我的平台上实际实现的东西,所以我选择了这个作为答案。谢谢大家。
      【解决方案6】:

      试试这个:

      Foo foo;
      foo = (Foo){bunch, of, things, initialized};
      

      如果您有一个好的编译器(例如 GCC),这将起作用。你可能需要用--std=gnu99打开C99模式,我不确定。

      【讨论】:

      • 非常酷,不幸的是,它不适用于我的(坏)编译器:MSVC2008。 :(
      • @jim : VC++ 2008 不是一个“糟糕”的编译器(尽管它肯定不是最好的);它只是不支持你想要的 C++11。
      • @ildjarn:我知道,这就是为什么我把“坏”放在括号中(我猜应该是引号)。我在戳大卫的暗示,如果你不能编译上面的,你有一个“坏”的编译器。无论哪种方式,我都不够精明,无法发表意见。
      • @jim - David 展示的聚合赋值语法是在 1999 年标准中引入的(是的,对于 gcc,您必须指定 --std=c99 或更高版本才能识别它)。不幸的是,微软决定不在 VS 套件中支持 C99。看看他们是否决定支持 C11 会很有趣。
      • @John MS 已经明确表示他们对开发 C 工具不感兴趣,只对原生方面的 C++ 感兴趣。
      猜你喜欢
      • 2011-01-19
      • 2012-07-29
      • 1970-01-01
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      相关资源
      最近更新 更多