57

I can do this on initialization for a struct Foo:

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

but, I can't do this:

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

So, two questions:

  1. Why can't I do the latter, is the former a special constructor for initialization only?
  2. How can I do something similar to the second example, i.e. declare a bunch of variables for a struct in a single line of code after it's already been initialized? I'm trying to avoid having to do this for large structs with many variables:

    Foo foo;
    
    foo.a = 1;
    foo.b = 2;
    foo.c = 3;
    //... ad infinitum
    
Chris Frederick
  • 5,482
  • 3
  • 36
  • 44
jim
  • 661
  • 1
  • 6
  • 8
  • 4
    "*is the former a special constructor for initialization only?*" Yes, precisely -- it's called _aggregate initialization_. – ildjarn Feb 13 '12 at 23:52
  • 13
  • 2
    C(++) remains very primitive in some areas. – Alexey Frunze Feb 13 '12 at 23:54
  • @Alex: It's just lower-level. That it appears "primitive in some areas" is actually just that it is the basic building blocks of procedural programming. Something has to be. "These red bricks are ugly. We should get rid of our bricks and come up with a new type of brick that is covered in white plaster." "So what goes underneath the plaster if not.. red bricks?" Red bricks still have to exist, and the people making the white plaster-covered bricks still have to see and work with the red bricks. – Lightness Races in Orbit Feb 13 '12 at 23:54
  • 1
    @LightnessRacesinOrbit: Precisely, thank you for elaborating. – Alexey Frunze Feb 13 '12 at 23:57
  • @LRiO: do you have a seperate solution for each? – jim Feb 14 '12 at 00:02
  • 3
    @jim : They're different languages and consequently warrant different questions. For _this_ question, choose a single language please. – ildjarn Feb 14 '12 at 00:05
  • @ildjarn: I'm interested in what the solutions might look like for either. Can you point me to where there is an SO rule that a question can only reflect a single language? I understand the thinking if I was referencing C++/OCAML, but, it would seem to me, a reader could derive value for a solution presented in either C/C++. – jim Feb 14 '12 at 00:18
  • @jim : Whether or not it's an SO rule, many people (myself included) won't answer a question tagged multiple languages because it implies an onus to give an answer for each language. If you want fewer answers to your question then that's certainly your prerogative. ;-] – ildjarn Feb 14 '12 at 00:20
  • 4
    @jim Asking for solutions in C and C++ makes it two different questions, albeit with common themes. You are only meant to ask one question at a time. I gave an answer for C, but when I realised that the question had both tags I deleted it. – David Heffernan Feb 14 '12 at 00:24
  • 1
    @ildjarn: Ok, well, that's unfortunate, because I really am interested in either, NOT both (no pressure). In my own code, I tend to mix the two, based on what I feel is the most appropriate for the situation vs. an all or nothing approach, but I understand your point of view. – jim Feb 14 '12 at 00:31
  • @jim: "LRiO" is not my name and, as such, "@LRiO" doesn't trigger a reply notification. – Lightness Races in Orbit Feb 14 '12 at 00:35
  • 1
    @jim: The two absolutely should _not_ be "mixed" like that. C and C++ are _distinct languages_ and _must_ be treated as such! OK so some solutions may overlap; many do not. – Lightness Races in Orbit Feb 14 '12 at 00:35
  • @LightnessRacesinOrbit: Apologies for not spelling your name out (it's quite long), I didn't realize notifications were triggered like that. As for your comment that the two should not be mixed like "that", I'm not sure what you're referring to, I gave no specific examples when I made that statement. If you're referring to my question, I can't comment, since I don't know what the different solutions are and where the lack of overlap may be, hence the separate tags in my question. – jim Feb 14 '12 at 00:49
  • @jim: "In my own code, I tend to mix the two" – Lightness Races in Orbit Feb 14 '12 at 01:04
  • @LightnessRacesinOrbit: Ah, I see, you were being universal. Well, good for you in your strict adherence to language boundaries, respect. – jim Feb 14 '12 at 01:35

6 Answers6

47

Try this:

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

This will work if you have a good compiler (e.g. GCC).

Update: In modern versions of C (but not C++), you can also use a compound literal with designated initializers, which looks like this:

foo = (Foo){ .bunch = 4, .of = 2, .things = 77, .initialized = 8 };

The name right after the "." should be the name of the structure member you wish to initialize. These initializers can appear in any order, and any member that is not specified explicitly will get initialized to zero.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • very cool, unfortunately, doesn't work on my (bad) compiler: MSVC2008. :( – jim Feb 13 '12 at 23:57
  • @jim : VC++ 2008 isn't a "bad" compiler (though it certainly isn't the best); it just doesn't support C++11, which is needed for what you want. – ildjarn Feb 13 '12 at 23:59
  • @ildjarn: i know, that's why i put "bad" in parethesis (should have been in quotes i guess). i was poking at David's implication, that if you can't compile the above, you have a "bad" compiler. i'm not savvy enough to have an opinion either way. – jim Feb 14 '12 at 00:06
  • 4
    @jim - the aggregate assignment syntax shown by David was introduced with the 1999 standard (and yes, with `gcc` you have to specify `--std=c99` or later for it to be recognized). Unfortunately, Microsoft has decided not to support C99 in the VS suite. It'll be interesting to see if they decide to support C11. – John Bode Feb 14 '12 at 00:15
  • @John MS have made it quite clear that they are not interested in developing C tooling and are only interested in C++ on the native side. – David Heffernan Feb 14 '12 at 00:25
  • 2
    Combined with @JohnHumpreys answer below, you could do something like: `foo = (Foo){.bunch = bunch, .of = of, .things = things, .initialized = initialized};` – kevinarpe Apr 04 '22 at 15:17
18

The first is an aggregate initializer - you can read up on those and tagged initializers at this solution:

What is tagged structure initialization syntax?

It is a special initialization syntax, and you can't do something similar after initialization of your struct. What you can do is provide a member (or non-member) function to take your series of values as parameters which you then assign within the member function - that would allow you to accomplish this after the structure is initialized in a way that is equally concise (after you've written the function the first time of course!)

Community
  • 1
  • 1
John Humphreys
  • 37,047
  • 37
  • 155
  • 255
  • I found this thread asking the same question: http://stackoverflow.com/questions/1705147/struct-initialization-of-the-c-c-programming-language. However, all of the answers, along with Davids (which I really liked and upvoted), seemed to be compiler specific. This is something I can actually implement on my platform, so I chose this as the answer. Thanks all. – jim Feb 14 '12 at 01:42
12

In C++11 you can perform multiple assignment with "tie" (declared in the tuple header)

struct foo {
    int a, b, c;
} f;

std::tie(f.a, f.b, f.c) = std::make_tuple(1, 2, 3);

If your right hand expression is of fixed size and you only need to get some of the elements, you can use the ignore placeholder with tie

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

If you find the syntax std::tie(f.a, f.b, f.c) too code cluttering you could have a member function returning that tuple of references

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);

All this ofcourse assuming that overloading the assignment operator is not an option because your struct is not constructible by such sequence of values, in which case you could say

f = foo(1, 2, 3);
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
4

Memory Footprint - Here is an interesting i386 addition.

After much hassle, using optimization and memcpy seems to generate the smallest footprint using i386 with GCC and C99. I am using -O3 here. stdlib seems to have all sorts of fun compiler optimizations at hand, and this example makes use of that (memcpy is actually compiled out here).

Do this by:

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;
}

Result:

  • (.rodata) FOO_ASSIGN_VAL is stored in .rodata
  • (.text) a sequence of *movl FOO_ASSIGN_VAL, %registers* occur
  • (.text) a sequence of movl %registers, foo occur

Example:

  • Say Foo was a 48 field struct of uint8_t values. It is aligned in memory.

  • (IDEAL) On a 32-bit machine, this COULD be as quick as 12 MOVL instructions of immediates out to foo's address space. For me this is 12*10 == 120bytes of .text in size.

  • (ACTUAL) However, using the answer by AUTO will likely generate 48 MOVB instructions in .text. For me this is 48*7 == 336bytes of .text!!

  • (SMALLEST*) Use the memcpy version above. IF alignment is taken care of,

    • FOO_ASSIGN_VAL is placed in .rodata (48 bytes),
    • 12 MOVL into %register
    • 12 MOVL outof %registers are used in .text (24*10) == 240bytes.
    • For me then this is a total of 288 bytes.

So, for me at least with my i386 code,

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

*Smallest here means 'smallest footprint I know of'. It also executes faster than the above methods (24 instructions vs 48). Of course, the IDEAL version is fastest & smallest, but I still can't figure that out.

-Justin

*Does anyone know how to get implementation of 'IDEAL' above? It is annoying the hell out of me!!

J-Dizzle
  • 4,861
  • 4
  • 40
  • 50
3

If you don't care too much about efficiency, you could double assign: i.e. create a new instance of the structure using aggregate initialization, and then copy it over:

struct Foo foo;

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

Make sure you keep the portion wrapped in {}s so as to discard the unnecessary temporary variable as soon as it's no longer necessary.

Note this isn't as efficient as making, e.g., a 'set' function in the struct (if c++) or out of the struct, accepting a struct pointer (if C). But if you need a quick, preferably temporary, alternative to writing element-by-element assignment, this might do.

Codesmith
  • 5,779
  • 5
  • 38
  • 50
  • Also compilers will optimize that. – Pavel Ognev May 08 '13 at 08:05
  • Why stop there? `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);` Stick it in a header file as `inline` and the performance impact should be minimal with optimization. The only downside is you can't leave out any things to be zeroed out at the end (though some may not consider that a downside given the existence of the `missing-field-initializers` warning). Of course, the `(Foo){...}` style is probably the simplest when it's available. – JAB Mar 24 '16 at 23:29
0

If you care about efficiency, you can define a union of the same length as your structure, with a type you can assign at once.

To assign values by elements use the struct of your union, to assign the whole data, use the other type of your union.

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

Be carefull about your memory organization (is "a" the MSB or the LSB of "whole"?).

atoMerz
  • 7,534
  • 16
  • 61
  • 101
Simon
  • 11