Combine designated initializers and malloc in C99 +?

Is there a good way to combine the assigned initializers from C99 with the result of malloc ?

The following seems to have unnecessary duplication:

 typedef struct { int a, b, c; } Type; Type *t = malloc(sizeof *t); *t = (Type) { .a = 2, .b = 3, .c = 5, }; 

Is it possible to remove Type and *t from the above code?

+6
source share
3 answers

As you asked;) in C there is one tool to avoid explicit duplication of code, macros. Nevertheless, I see no way not to repeat at least the type name. But in C ++ they can't either, so C is at least as good :)

The lightest I see

 #define DESIGNATE_NEW(T) \ memcpy(malloc(sizeof(T)), \ &(T const){ __VA_ARGS__ }, \ sizeof(T)) 

which would give

 Type *t = DESIGNATE_NEW(Type, .a = 2, .b = 3, .c = 5, ); 

This has several advantages.

  • Initializes all elements correctly, even on architectures, standard 0 representations for float types or pointers.
  • Unlike the Keith version, this β€œcoding style” is acceptable, because it is just an expression that looks like initialization, and someone should immediately visually display what the second code skippet should do.

Note. Observe the const in the macro, this allows you to discard multiple instances of the composite literal if the compiler decides that this is relevant. There is also a means to have an option where a list of pointers is optional, see Below P99.

The downside is memcpy , and I would be happier with the appointment. Secondly, there is no malloc failure check before using the result, but you can probably run into some kind of weirdness in order to get good code.

In P99, I am a little different. There we always have an initialization function for the type, something like

 inline Type* Type_init(Type* t, int a, int b, int c) { if (t) { *t = (Type const){ .a = a, .b = b, .c = c }; } return t; } 

which macros can be made to provide default arguments for a , b and c if omitted. Then you can just use something like

 Type *t = P99_NEW(Type, 1, 2, 3); 

in your application code. This is better since when calling malloc it was not possible to delete the pointer to the pointer. On the other hand, this reintroduces the order to the initializers, therefore is also not perfect.

+5
source

You can use a variable macro. I will not argue that this is a good idea, but it works:

 #include <stdlib.h> #include <stdio.h> #define CREATE(type, ptr, ...) \ type *ptr = malloc(sizeof *ptr); \ if (ptr) *ptr = (type){__VA_ARGS__} int main(void) { typedef struct { int a, b, c; } Type; CREATE(Type, t, .a = 2, .b = 3, .c = 5); printf("t->a = %d, t->b = %d, t->c = %d\n", t->a, t->b, t->c); return 0; } 

Please note that I could not use the usual trick of defining the macro do { ... } while (0) (it will create a new scope, and t will not be visible), so you need to be careful in the context in which you use this.

Personally, I think I'm happy with unnecessary duplication.

+2
source

No, this is the only way to use designated initializers. Without (Type) {}, the compiler does not know how to check the contents.

+1
source

Source: https://habr.com/ru/post/896370/


All Articles