67
typedef struct {
    int hour;
    int min;
    int sec;
} counter_t;

And in the code, I'd like to initialize instances of this struct without explicitly initializing each member variable. That is, I'd like to do something like:

counter_t counter;
counter = {10,30,47}; //doesn't work

for 10:30:47

rather than

counter.hour = 10;
counter.min = 30;
counter.sec = 47;

Don't recall syntax for this, and didn't immediately find a way to do this from Googling.

Thanks!

mindthief
  • 12,755
  • 14
  • 57
  • 61
  • 1
    Yeah looks like it works if I do the declaration in the same line like so `counter_t counter = {10,30,47}` but not if the declaration has been done before this assignment. – mindthief Jan 23 '11 at 23:52
  • 5
    @Oli: Why should that work? As written that's an assignment, not an initialization. – sth Jan 23 '11 at 23:53
  • 2
    @sth: It seems the question has been modified... – Oliver Charlesworth Jan 23 '11 at 23:54
  • As alluded in Steve's answer, what you have is *assignment* rather than *initialization* (as you said in the title). C blurs these lines much more than C++ does, but as Oli also points out, initialization (in both C and C++) already works as you expect. – Fred Nurk Jan 24 '11 at 00:47
  • 3
    @Oli @sth: yes I was wondering if you might have seen it before the modification. For a very brief period (about a minute) I had the working version of the statement up, i.e. `counter_t counter = {10,30,47};`. I changed it when I realized that it actually worked :). In any case, what I really wanted was to declare it separately from the assignment. @Fred Nurk: good call, I'll change the title to say "assign". Thanks! – mindthief Jan 24 '11 at 01:13

3 Answers3

106

Initialization:

counter_t c = {10, 30, 47};

Assignment:

c = (counter_t){10, 30, 48};

The latter is called a "compound literal".

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 9
    Note that this requires either GCC or C99, not a strict C89 or C++ compiler. – Jeremiah Willcock Jan 23 '11 at 23:48
  • 16
    @Jeremiah: good point, I didn't notice the C++ tag. As for C89, if people go around saying "C" when they mean "C89", they're on their own as far as I'm concerned ;-) – Steve Jessop Jan 23 '11 at 23:50
  • Nice, yeah I just figured it out too -- used counter = (counter_t){10,30,47} which is the same as what you suggested. "Compound literal" eh? Thanks! – mindthief Jan 23 '11 at 23:51
  • @Steve, a lot of the embedded world is stuck with expensive compilers that are C89 and may never become C99. There is some creep in the direction of using GCC built as a cross compiler instead, but change is often slow. – RBerteig Jan 24 '11 at 00:00
  • @RBerteig: I see that mentioned so often that it seems there would be a market for a C99 to C89 compiler. – Fred Nurk Jan 24 '11 at 00:03
  • 2
    @RBerteig: no doubt. I'm not saying nobody should use C89, I'm saying that if they can't use C99 then they shouldn't just say that what they're using is "C", without further warning. – Steve Jessop Jan 24 '11 at 00:08
  • @Steve, that probably does make sense. but after too many years building embedded systems in C, I tend to even forget that there have been revisions to the standard. I'm also growing weary of the common belief that C and C++ are the same language, with a few warts and features tacked on. – RBerteig Jan 24 '11 at 00:13
  • 1
    @RBerteig: well, consider me weary of the common belief that C89 and C99 are the same language. It's the C89 serfs who should have to add the extra qualifications ;-) I say this as someone who was writing C89-only code myself as recently as 2008. – Steve Jessop Jan 24 '11 at 00:16
  • 1
    @Fred: is it that easy, though? For example if the platform doesn't provide at least `alloca`, then your C99 compiler has to dynamically allocate VLAs, which is trouble. Once you're not playing nicely with the target's stack, `setjmp` might be entertaining to implement. You could say, "well do a C89 backend for the compiler, let that handle everything". I wonder whether an *efficient* backend in C89 for, say, GCC, is possible. If not efficient then for embedded work it's probably dead in the water, "no C99 in the vendor's compiler" is just one of several tight limitations such devices have... – Steve Jessop Jan 24 '11 at 00:22
  • 1
    @Steve I don't know about you, but I cringe whenever I see someone use VLAs, `alloca`, or `setjmp`. VLAs seem to rely on alloca, or similar functionality. The man page for `alloca` indicates that it isn't guaranteed to do what it should. http://linux.die.net/man/3/alloca see bugs section. Use of `setjmp` makes it damn near impossible to do meaningful static analysis, either automated or by hand. If you're going to program like you're in assemble, use assembly. – KitsuneYMG Jan 24 '11 at 00:30
  • @Kitsune: I agree about VLAs, I don't like them, but if I'm writing a C99 compiler then my opinion doesn't really enter into it. Careful use of `setjmp/longjmp` can actually make C *higher* level, not lower, if you're using it to provide an exception-like mechanism, so I don't reject it on the same grounds. Emphasis on "careful", though, you have to know what you're doing wrt resource handling, and I suppose the temptation to use VLAs might creep in there because it saves allocating a buffer and adding it to some cleanup list. – Steve Jessop Jan 24 '11 at 00:36
  • @Steve: I don't know how easy it would be. I'm sure there are lots of corner cases to consider (especially in the stdlib? such as the floating point additions?), but it should be mostly possible to get generic, "more useful" features (e.g. the transformation for compound literals as in your answer, or removing the variable declaration location restriction, or even minor things like one-line comments), then require special backend (i.e. the embedded compiler) support for the rest. Though my experience with C++ (especially around the time of VC6) leads me to be very leery of partial support. – Fred Nurk Jan 24 '11 at 00:42
  • @Fred: floating-point-wise I don't immediately see any problems, because supporting IEEE float is still optional in C99. If necessary you can set all the preprocessor macros that say, "nope, my float support is rubbish", then even though you're using IEEE *format* you aren't obliged to support all the features. `//` comments are trivial, just a simple pre-preprocessor pass, totally portable and can't harm performance. Other new features could be switched on and off as a configuration thing, to "prettify" code written for the platform rather than with any expectation of porting proper C99 code. – Steve Jessop Jan 24 '11 at 00:47
44

For the sake of maintainability I prefer the list syntax WITH explicitly identified variables, as follows:

counter_t counter = {.hour = 10, .min = 30, .sec = 47};

or for returning inline for example:

return (struct counter_t){.hour = 10, .min = 30, .sec = 47};

I can imagine a scenario where one changes the order in which the variables are declared, and if you don't explicitly identify your variables you would have to go through all the code to fix the order of variables. This way it is cleaner and more readable

MuhsinFatih
  • 1,891
  • 2
  • 24
  • 31
  • 2
    I like this too but not so good if you wish to also compile with a c++ compiler as is a C99 only feature that is not supported by c++. https://stackoverflow.com/questions/12122234/how-to-initialize-a-struct-using-the-c-style-while-using-the-g-compiler – Ashley Duncan Dec 05 '17 at 20:27
  • Didn't know that. It's a shame though, it is such a useful feature. The best approach I think in this case is Don Doerner's solution: https://stackoverflow.com/a/14206226/2770195 . Simply put: Use classes with variables and a single initializer. Doesn't seem like a good practice, but does exactly what structs do. Going to update the answer with clarification, thanks for reminding – MuhsinFatih Dec 05 '17 at 21:29
  • I am confused, I just compiled an example with -gnu++11 option: https://1drv.ms/i/s!ArNqKg0GFwIE0HVGDPPHvpEgaGrh . Am I missing something? – MuhsinFatih Dec 05 '17 at 21:47
  • @AshleyDuncan any ideas? – MuhsinFatih Dec 07 '17 at 13:52
  • @MuhsinFatih `gnu++11` means to use GNU extensions – M.M Dec 09 '17 at 01:31
  • @M.M yes, but I just compiled the example with c++. You said, and the page you suggested confirms, that c++ does not support list syntax with explicitly identified variables (I mean the example I have given in my answer) – MuhsinFatih Dec 09 '17 at 06:26
  • @MuhsinFatih I am not sure what you are confused about. This syntax does not work in ISO C++ but does work in GNU C++. – M.M Dec 09 '17 at 06:28
  • @M.M Oh I see. I just tried it with clang (`clang -std=c++11 -o main main.cpp`), got an `undefined symbol error`. Am I doing this right? If so I will update the answer to mention this issue – MuhsinFatih Dec 09 '17 at 07:02
  • @M.M I should have used `clang++`. clang++ does support this. If you know of an example please suggest an edit – MuhsinFatih Dec 15 '17 at 15:52
1

As a complement to @MuhsinFatih's answer, and with more authoritative references.

We can use C99's Designator to achieve the goal, just like what @MuhsinFatih has suggested:

counter_t counter = {.hour = 10, .min = 30, .sec = 47};

The same rule applies to Array as well:

int num[5] = { [0]=123, [2]=456, [4]=789 };

printf("%d %d %d %d %d \n",num[0], num[1], num[2], num[3], num[4]);
// Output:
//    123 0 456 0 789 
Michael Lee
  • 197
  • 8