static (non-zero) initialization of an array of structs (array of length #define LEN)

Discussion in 'C Programming' started by anon.asdf, Aug 27, 2007.

  1. anon.asdf

    anon.asdf Guest

    Hello!

    In the following code-snippet, is it possible to initialize each
    element of arr, with STRUCT_INIT?

    struct mystruct {
    int a;
    char b;
    };

    #define STRUCT_INIT {5, 'a'}

    #define LEN 3
    struct mystruct arr[LEN] = \
    {STRUCT_INIT};
    /* does not work: only 1st element initialized as desired */

    /*
    I don not want to write
    {STRUCT_INIT, STRUCT_INIT, STRUCT_INIT};
    since LEN can change!
    !!!!!!!!!
    */

    ..
    ..
    Can this be done?

    Thanks
    -Albert

    ..
    ..
    ..
    ..

    /****** test-program ******/
    #define <stdio.h>
    int main(void)
    {
    int i;

    for (i = 0; i < LEN; i++)
    printf("|%d|, |%c|\n", arr.a, arr.b);
    return 0;
    }
     
    anon.asdf, Aug 27, 2007
    #1
    1. Advertisements

  2. anon.asdf

    anon.asdf Guest

    Perhaps it can be done with the preprocessor.
    I gave it a try, but I'm no preprocessor expert - the code below
    results in compiler (preprocessor) errors.

    But: Could something along these lines, be done:
    #define STRUCT_INIT {5, 'a'}

    #define PUT_A(n) \
    #if ((n) == 1) \
    STRUCT_INIT \
    #elif ((n) > 0) \
    STRUCT_INIT , PUT_B((n)-1) \
    #endif

    #define PUT_B(n) \
    #if ((n) == 1) \
    STRUCT_INIT \
    #elif ((n) > 0) \
    STRUCT_INIT , PUT_A((n)-1) \
    #endif


    #define LEN 3
    struct mystruct arr[LEN] = \
    {PUT_B(LEN)};


    Thanks -Albert
     
    anon.asdf, Aug 27, 2007
    #2
    1. Advertisements

  3. <snip>

    You are putting a lot of effort to avoid the simplest for loop. Why?
    Just initialise your data in a loop.
     
    Ben Bacarisse, Aug 27, 2007
    #3
  4. anon.asdf

    anon.asdf Guest

    Good question - it made me think!
    Here's a scenario: If you have code running at boot time and want to
    boot as fast as possible!
    True. -Albert
     
    anon.asdf, Aug 27, 2007
    #4
  5. How big is this array anyway? I'd say that if it's less than 1 MB it should
    take less than a second. I think that performance should only be considered
    when the code is run very frequently and is time-consuming. Not when you're
    running it just once and at the initialization phase where users expect the
    program to spend some time (a second or two) to initialize.

    Abdo Haji-Ali
    Programmer
    In|Framez
     
    Abdo Haji-Ali, Aug 27, 2007
    #5
  6. anon.asdf

    Eric Sosman Guest

    Which do you think is faster: Running a loop that
    copies constant data into a million array slots, or
    reading a million copies of that data from the boot
    device?
     
    Eric Sosman, Aug 27, 2007
    #6
  7. <snip>
    If you can put this in a separate C file, you could write
    a wee program to write that C file, given LEN, and invoke it
    as part of your build process.

    Off topic: if you use gcc and don't care about portability to other
    compilers, you could write
    struct mystruct arr[] = { [0 ... LEN-1 ] = STRUCT_INIT};
    That's not C but a gcc extension.
     
    Duncan Muirhead, Aug 27, 2007
    #7
  8. anon.asdf

    Dave Hansen Guest

    All the world is not a hosted system. And many of us deal with
    software that is not loaded from disk, memory that cannot be written
    at runtime, and RAM that is scarce and expensive. Of course, in those
    situations, you rarely (never?) want all the elements of the array to
    contain identical data.

    This is one of C's few real shortcomings for writing embedded
    software.

    The standard way to (or at least, the way I usually) overcome this is
    to #define LEN in a header file, and write a simple PC application
    that can be invoked by the makefile to generate the initialization
    data and save it to a file, so the code becomes

    #include "len.h"

    struct mystruct arr[LEN] = {
    #include "initarr.dat"
    };

    Regards,

    -=Dave
     
    Dave Hansen, Aug 27, 2007
    #8
  9. anon.asdf

    Flash Gordon Guest

    Eric Sosman wrote, On 27/08/07 16:04:
    It depends. And there are situations I am aware of which almost
    certainly do *not* apply to the OP where the bootup time *is* critical.
     
    Flash Gordon, Aug 27, 2007
    #9
  10. Yes, it was a genuine question. I thought it prudent to check, first,
    that there *was* reason to go to all the effort of statically
    initialising the array.
     
    Ben Bacarisse, Aug 27, 2007
    #10
  11. That's gratifying -- I fixed the gutters *and* asked a good question.
    Today is a Good Day!
    Maybe, maybe, not. It is almost always better to write clear, simple
    code first and then make a mess of it if it really is too slow. In
    other words the wise programmer will try to ensure that the answer to
    a question like this will always be "I used a loop and it was bad
    because...".
     
    Ben Bacarisse, Aug 27, 2007
    #11
  12. anon.asdf

    Dave Hansen Guest

    Since you asked...

    I think reading the data from the boot device is faster than allowing
    the startup code to zero the array first (as it must do with
    uninitialized static data), then explicitly re-initializing the array
    at run time. The larger the array, the more significant the
    difference.

    Although zeroing the memory is certainly (well, at least _usually_)
    much faster than either initialization procedure...

    Regards,

    -=Dave
     
    Dave Hansen, Aug 27, 2007
    #12
  13. anon.asdf

    Eric Sosman Guest

    Dave Hansen wrote On 08/27/07 17:24,:
    <topicality level="dwindling">

    Seagate calls their Savvio 15K drive the "fastest [...]
    in the world," so let's take it as indicative of the higher
    range of today's disk speeds. The data sheet gives sustained
    transfer rates of between 78 and 112 MB/s, depending on what
    part of the platter is used. Assuming that there's no seek
    time and no latency (both absorbed by something else we must
    read anyhow), the best we can hope for is thus 8.9 ns/byte.

    Now to the CPU. Let's assume it runs a loop that stores
    eight bytes (the probable size of the O.P.'s struct) per
    iteration. To break even with the disk it must complete an
    iteration every 71.4 ns. On a 2 GHz machine, that's 142.9
    cycles, and since the loop will easily fit in the cache and
    since its branch prediction will be almost always right, we
    can expect to issue at least one instruction per cycle. So
    to beat the disk, all we need is a compiler that translates

    for (i = 0; i < LEN; ++i) {
    arr.a = 5;
    arr.b = 'a';
    }

    .... into fewer than 140 instructions per iteration.

    YMMV, of course. But on the assumption that people
    usually don't lay out big bucks for jackrabbit disks only to
    pair them with tortoise-like CPU's (the aforementioned Savvio
    goes for $455-655, sez the Web, while $90 gets you an AMD
    Athlon at 2.1 GHz), I'd expect to find broadly similar
    relations in most systems.

    (Of course, the questioner never said he was booting
    from a disk, either: All this numerical noodling may be
    nothing but nitwittical nonsense.)

    </topicality>
     
    Eric Sosman, Aug 27, 2007
    #13
  14. [...]

    Yes. You can create meta-lists and expand then using macros. The format is
    something like the following most basic example:

    #define NULL_NODE
    #define YOUR_LIST() ({1, 2}, ({3, 4}, ({5, 6}, NULL_NODE)))


    you can expand the list like this:


    #define LIST_EXPAND_LVL_3(_node, _next) \
    _node

    #define LIST_EXPAND_LVL_1(_node, _next) \
    _node , LIST_EXPAND_LVL_2 _next

    #define LIST_EXPAND_LVL_0(_node, _next) \
    _node , LIST_EXPAND_LVL_1 _next

    #define LIST_EXPAND_BEGIN(_list) \
    LIST_EXPAND_LVL_0 _list


    use it like:

    LIST_EXPAND_BEGIN(YOUR_LIST())

    this will output:

    {1, 2} , {3, 4} , {5, 6}


    There is a robust example of this in the boost pre-processing library. So,
    there is a solution for you with the C preprocessor. Perhaps I will post
    some of my older code here that shows off a more complete version of
    meta-lists. Its more lightweight than boost stuff. I need to find the code
    and clean it off a bit. BTW, would anybody be interested?
     
    Chris Thomasson, Aug 27, 2007
    #14
  15. anon.asdf

    CBFalconer Guest

    And you get a significantly different answer on systems that use
    'copy on write' segments. As usual, that is a system environment
    issue, and not germane to c.l.c.
     
    CBFalconer, Aug 28, 2007
    #15
  16. You can also use something like this:

    #define CONCAT_X(_t1, _t2)_t1##_t2
    #define CONCAT(_t1, _t2)CONCAT_X(_t1, _t2)


    #define PLACE_X(_t)_t
    #define PLACE_1(_t)PLACE_X(_t)
    #define PLACE_2(_t)PLACE_X(_t)PLACE_X(_t)
    #define PLACE_3(_t)PLACE_X(_t)PLACE_X(_t)PLACE_X(_t)
    #define PLACE_4(_t)PLACE_X(_t)PLACE_X(_t)PLACE_X(_t)PLACE_X(_t)

    /* and on and on to cover the depths you desire */

    #define PLACE(_t, _d)CONCAT(PLACE_, _d)(_t)


    Then do something like:

    #define LEN() 3

    typedef struct foo_s {
    int i;
    char a;
    } foo_t;


    #define FOO_STATICINIT() { \
    PLACE({1, 'x'}, LEN()) \
    }


    static foo_t g_foo = FOO_STATICINIT();


    Would that work for you?
     
    Chris Thomasson, Aug 28, 2007
    #16
  17. [...]
    [...]

    You can amortize the length the PLACE_ macro functions by doing something
    like this:

    #define PLACE_X(_t)_t
    #define PLACE_1(_t)PLACE_X(_t)
    #define PLACE_2(_t)PLACE_X(_t)PLACE_X(_t)
    #define PLACE_3(_t)PLACE_2(_t)PLACE_X(_t)
    #define PLACE_4(_t)PLACE_3(_t)PLACE_X(_t)
    #define PLACE_5(_t)PLACE_4(_t)PLACE_X(_t)
    #define PLACE_6(_t)PLACE_5(_t)PLACE_X(_t)
    #define PLACE_7(_t)PLACE_6(_t)PLACE_X(_t)
    #define PLACE_8(_t)PLACE_7(_t)PLACE_X(_t)
    #define PLACE_9(_t)PLACE_8(_t)PLACE_X(_t)
    #define PLACE_10(_t)PLACE_9(_t)PLACE_X(_t)
    #define PLACE_11(_t)PLACE_10(_t)PLACE_X(_t)
    #define PLACE_12(_t)PLACE_11(_t)PLACE_X(_t)
    #define PLACE_13(_t)PLACE_12(_t)PLACE_X(_t)
    #define PLACE_14(_t)PLACE_13(_t)PLACE_X(_t)
    #define PLACE_15(_t)PLACE_14(_t)PLACE_X(_t)
    #define PLACE_16(_t)PLACE_15(_t)PLACE_X(_t)
    #define PLACE_17(_t)PLACE_16(_t)PLACE_X(_t)
    #define PLACE_18(_t)PLACE_17(_t)PLACE_X(_t)
    #define PLACE_19(_t)PLACE_18(_t)PLACE_X(_t)
    #define PLACE_20(_t)PLACE_19(_t)PLACE_X(_t)

    /* and on and on to suite your needs... */

    :^)
     
    Chris Thomasson, Aug 28, 2007
    #17
  18. Oops! I made two mistakes:

    1: forgot to make g_foo an array!

    2: forgot to add commas in the place macros!

    So, here is a re-post of the pseudo-code:

    #define CONCAT_X(_t1, _t2)_t1##_t2
    #define CONCAT(_t1, _t2)CONCAT_X(_t1, _t2)


    #define PLACE_X(_t)_t
    #define PLACE_1(_t)PLACE_X(_t)
    #define PLACE_2(_t)PLACE_X(_t), PLACE_X(_t)
    #define PLACE_3(_t)PLACE_2(_t), PLACE_X(_t)
    #define PLACE_4(_t)PLACE_3(_t), PLACE_X(_t)
    #define PLACE_5(_t)PLACE_4(_t), PLACE_X(_t)
    #define PLACE_6(_t)PLACE_5(_t), PLACE_X(_t)
    #define PLACE_7(_t)PLACE_6(_t), PLACE_X(_t)
    #define PLACE_8(_t)PLACE_7(_t), PLACE_X(_t)
    #define PLACE_9(_t)PLACE_8(_t), PLACE_X(_t)
    #define PLACE_10(_t)PLACE_9(_t), PLACE_X(_t)
    #define PLACE_11(_t)PLACE_10(_t), PLACE_X(_t)
    #define PLACE_12(_t)PLACE_11(_t), PLACE_X(_t)
    #define PLACE_13(_t)PLACE_12(_t), PLACE_X(_t)
    #define PLACE_14(_t)PLACE_13(_t), PLACE_X(_t)
    #define PLACE_15(_t)PLACE_14(_t), PLACE_X(_t)
    #define PLACE_16(_t)PLACE_15(_t), PLACE_X(_t)
    #define PLACE_17(_t)PLACE_16(_t), PLACE_X(_t)
    #define PLACE_18(_t)PLACE_17(_t), PLACE_X(_t)
    #define PLACE_19(_t)PLACE_18(_t), PLACE_X(_t)
    #define PLACE_20(_t)PLACE_19(_t), PLACE_X(_t)


    /* and on and on to cover the depths you desire */

    #define PLACE(_t, _d)CONCAT(PLACE_, _d)(_t)


    Then do something like:

    #define LEN() 3

    typedef struct foo_s {
    int i;
    char a;
    } foo_t;


    #define FOO_STATICINIT() { \
    PLACE({1, 'x'}, LEN()) \
    }


    static foo_t g_foo = FOO_STATICINIT();
     
    Chris Thomasson, Aug 28, 2007
    #18
  19. [...]

    Fu%cking damn! There is an error with the PLACE macros because {1, 'x'} is
    going to be treated as two parameters. The trick is to pass the PLACE macro
    a name to a macro function that it calls to get at the tokens. I am going to
    post some working code in about 10-15 minutes. Please hold...
     
    Chris Thomasson, Aug 28, 2007
    #19
  20. [...]


    Here is a possible solution in the form of working code:

    --------------

    #include <stdio.h>


    #define CONCAT_X(mp_t1, mp_t2)mp_t1##mp_t2
    #define CONCAT(mp_t1, mp_t2)CONCAT_X(mp_t1, mp_t2)


    #define PLACE_X(mp_fp)mp_fp()
    #define PLACE_1(mp_fp)PLACE_X(mp_fp)
    #define PLACE_2(mp_fp)PLACE_X(mp_fp), PLACE_X(mp_fp)
    #define PLACE_3(mp_fp)PLACE_2(mp_fp), PLACE_X(mp_fp)
    #define PLACE_4(mp_fp)PLACE_3(mp_fp), PLACE_X(mp_fp)
    #define PLACE_5(mp_fp)PLACE_4(mp_fp), PLACE_X(mp_fp)
    #define PLACE_6(mp_fp)PLACE_5(mp_fp), PLACE_X(mp_fp)
    #define PLACE_7(mp_fp)PLACE_6(mp_fp), PLACE_X(mp_fp)
    #define PLACE_8(mp_fp)PLACE_7(mp_fp), PLACE_X(mp_fp)
    #define PLACE_9(mp_fp)PLACE_8(mp_fp), PLACE_X(mp_fp)
    #define PLACE_10(mp_fp)PLACE_9(mp_fp), PLACE_X(mp_fp)
    #define PLACE(mp_fp, mp_d)CONCAT(PLACE_, mp_d)(mp_fp)


    typedef struct foo_s {
    int i;
    char a;
    } foo_t;


    #define FOO_STATICINIT(mp_fp, mp_d) { \
    PLACE(mp_fp, mp_d) \
    }


    #define MYARRAY_DEPTH_1() 4
    #define MYARRAY_DEPTH_2() 7
    #define MYARRAY_INIT_1() {1, 'x'}
    #define MYARRAY_INIT_2() {5, 'a'}


    static foo_t g_foo1[MYARRAY_DEPTH_1()] =
    FOO_STATICINIT(MYARRAY_INIT_1, MYARRAY_DEPTH_1());


    static foo_t g_foo2[MYARRAY_DEPTH_2()] =
    FOO_STATICINIT(MYARRAY_INIT_2, MYARRAY_DEPTH_2());


    int main(void) {
    {
    int i;

    for(i = 0; i < MYARRAY_DEPTH_1(); ++i) {
    printf("g_foo1[%i].i(%i)\ng_foo1[%i].a(%c)\n----\n",
    i, g_foo1.i, i, g_foo1.a);
    }

    puts("\n\n_____________________________\n\n");

    for(i = 0; i < MYARRAY_DEPTH_2(); ++i) {
    printf("g_foo2[%i].i(%i)\ng_foo2[%i].a(%c)\n----\n",
    i, g_foo2.i, i, g_foo2.a);
    }
    }

    puts("\n\n\n\
    _______________________\npress <enter> to exit...\n");
    return getchar();
    }
     
    Chris Thomasson, Aug 28, 2007
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.