Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialise a complex C struct in one line?

I have a structure:

typedef struct stock {
  const char* key1p2;    // stock code
  const char* key2p2;    // short desc
  const char* desc1;     // description
  const char* prod_grp;  // product group
  const char dp_inqty;   // decimal places in quantity
  const long salprc_u;   // VAT excl price
  const long salprc_e;   // VAT includive price
  const long b_each;     // quantity in stock
  const long b_alloc;    // allocated qty
  const char* smsgr_id;  // subgroup
  const char** barcodes; // barcodes
} stock_t;

and I want to initialise arrays of instances of this structure, in one line of code per stock struct.

I have tried:

stock_t data_stock[] = {
    { "0001", "Soup",  "Tomato Soup",     "71", 0, 100, 120, 10, 0, "",   {"12345", "23456", NULL} },
    { "0002", "Melon", "Melon and Ham",   "71", 0, 200, 240, 10, 0, "",   {"34567", "45678", NULL} },
    ...
    { NULL,   NULL,    NULL,              NULL, 0,   0,   0,  0, 0, NULL, NULL         }
};

but it fails with:

data.c:26:74: warning: incompatible pointer types initializing 'const char **' with an expression of type 'char [6]'
      [-Wincompatible-pointer-types]
  { "0001", "Soup",  "Tomato Soup",     "71", 0, 100, 120, 10, 0, "",   {"12345", "23456", NULL} },
                                                                         ^~~~~~~

It is the barcode field which is problematic, as it is char**.

(that was clang, but GCC reports a similar error, but less helpfully.)

It's almost as if the compiler has ignored the curly brace before "12345".

I can work around the problem using:

const char *barcodes0001[] = {"12345", "23456", NULL};

stock_t data_stock[] = {
  { "0001", "Soup",  "Tomato Soup",     "71", 0, 100, 120, 10, 0, "",   barcodes0001 },

Is the cause of this problem a different between char [] and char *, or is there something more subtle. (Perhaps you can initialise arrays of structs, but not structs of arrays.)

like image 495
fadedbee Avatar asked Dec 20 '22 19:12

fadedbee


2 Answers

It's not as if the compiler is ignoring your brace, it's as if your brace is ignoring the syntax rules of the language, rather. :)

The field barcode is a single pointer (to a pointer, but that's beside the point). You need to provide a valid pointer value, and the braced thing you're providing doesn't match.

You also can't do this:

struct foo {
  int a, b;
};

struct foo *pointer_to_foo = &{ 1, 2 }; /* Not valid code. */

Which is logically the same as what you're trying to do. Or, removing the struct, you also can't do this:

int *pointer_to_a = &12; /* Not valid code. */

The solution where you've broken out the barcode data is the way to do it.

like image 83
unwind Avatar answered Dec 24 '22 00:12

unwind


To avoid to have to declare a named dummy variable you can use a compound literal

stock_t data_stock[] = {
    { "0001", "Soup",  "Tomato Soup",     "71", 0, 100, 120, 10, 0, "",   (const char*[]){"12345", "23456", NULL} },
    { "0002", "Melon", "Melon and Ham",   "71", 0, 200, 240, 10, 0, "",   (const char*[]){"34567", "45678", NULL} },
    ...
    { NULL,   NULL,    NULL,              NULL, 0,   0,   0,  0, 0, NULL, NULL         }
};

This is a way to define a local temporary variable with a syntax of (type){ initializers } which is available since c99 (and clang and gcc have them).

Edit: The lifetime of these compound literals is the same as your variable data_stock so I suppose this is ok. In any case I think you should mark most of your fields also as const, e.g const char*const key1p2;

Also it would be much easier to read if you'd use designated initializers something like

{ .keyp1 = "0001", ... }
like image 24
Jens Gustedt Avatar answered Dec 24 '22 01:12

Jens Gustedt