Flexible arrays - v confused

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mechanicfem@googlemail.com

    Flexible arrays - v confused

    I thought (as they're in c99) that flexible arrays were there for a
    number of reasons - but I also thought they'd be great for stepping
    into structures (say) that were aligned in memory after (say) a header
    struct, e.g.:

    struct element
    {
    int a;
    int b;
    };
    typedef struct element elem;

    struct header
    {
    int a;
    elem element[];
    };
    typedef struct header head;
    ....

    head * p;
    ....

    (p -> element)[10] = ...;

    However, the same 'trick' can be done if header contained this

    elem * element;
    };

    So, I can't really see the benefit of using empty array notation here.
    And anyway, how would you malloc some memory for element when it's an
    array type - surely p -> element = malloc... is an error, as element is
    an array, and so can't be assigned to? That leaves me thinking that to
    get it to work like that, maybe you should malloc a lump of memory,
    then cast the address to a header *, access element, and then index
    into that. But again, you could do this using straight forward pointer
    notation in the declaration of element?

    So, then I thought, well, maybe the notation would allow me to
    initialise a struct ...

    struct foo
    {
    int a;
    char b[];
    };

    struct foo s = {10, {'f', 'o', 'o'}};

    But gcc doesn't like that (with -std=c99), and again, if b were
    declared char * b, surely this is illegal ...

    struct foo s = {10, {"foo"}};

    So, Istarting from 'they must be great for a number of reasons' I'm now
    totally confused as to what flexible arrays can do for you. Could
    someone please help me?

    x

    Jo

  • Zero

    #2
    Re: Flexible arrays - v confused

    #include <stdio.h>
    #include <stdlib.h>

    struct element
    {
    int a;
    int b;
    };

    typedef struct element elem;

    struct header
    {
    int a;
    elem element[];
    };

    typedef struct header head;





    int main(int argc, char *argv[])
    {


    head *p;


    p = malloc ( sizeof(struct header) + sizeof(struct element) * 10);


    system("PAUSE") ;
    return 0;
    }


    This declares a pointer p of type head
    which allocates memory for one struct header and 10 elements
    of type struct element.

    Comment

    • Default User

      #3
      Re: Flexible arrays - v confused

      Zero wrote:

      [color=blue]
      > This declares a pointer p of type head
      > which allocates memory for one struct header and 10 elements
      > of type struct element.[/color]

      That's nice, so what? (See below).



      Brian

      --
      Please quote enough of the previous message for context. To do so from
      Google, click "show options" and use the Reply shown in the expanded
      header.

      Comment

      • Chris Torek

        #4
        Re: Flexible arrays - v confused

        In article <1139922094.022 699.95880@g47g2 000cwa.googlegr oups.com>
        <mechanicfem@go oglemail.com> wrote:[color=blue]
        >I thought (as they're in c99) that flexible arrays were there for a
        >number of reasons ...[/color]

        They really exist for just one reason: to legitimize the old
        "struct hack" (as it is called in the FAQ).

        To review for a moment, the "struct hack" is done something like this:

        #include <stdlib.h>

        struct vector {
        size_t n; /* number of values in the vector */
        double val[1]; /* actually size n */
        };

        struct vector *vec_new(size_t n) {
        struct vector *p;

        /* need n-1 here because the array has an "extra" element */
        p = malloc(sizeof *p + (n ? n - 1 : 0) * sizeof p->val[0]);
        if (p != NULL) {
        p->n = n;
        while (n)
        p->val[--n] = 0.0;
        }
        return p;
        }

        void vec_free(struct vector *p) {
        free(p);
        }

        double vec_access(stru ct vector *p, size_t i) {
        if (i >= p->n)
        panic("vec_acce ss: nonexistent element %lu\n", (unsigned long)i);
        return p->val[i];
        }

        To use a C99 "flexible array" structure member, we simply remove
        the constant "1" and get rid of the code that subtracts 1 from the
        computation passed to malloc(). This simplifies vec_new:

        p = malloc(sizeof *p + n * sizeof p->val[0]);

        and otherwise has no effect on the machine-level code produced,
        except that it *must* actually work, whereas the pre-C99 version
        was allowed to produce code that misbehaved at runtime. (That is,
        the "struct hack" is not 100% legitimate, although no one has
        produced examples of machines on which it does not work.)
        [color=blue]
        >However, the same 'trick' can be done if [the struct that has the
        >Flexible Array Member] contained [a pointer] ...[/color]

        In other words, we could equally write:

        struct vector {
        size_t n;
        double *val;
        };

        struct vector *vec_new(size_t n) {
        struct vector *p;

        p = malloc(sizeof *p);
        if (p != NULL) {
        p->val = malloc(n * sizeof *p->val);
        if (p->val != NULL) {
        p->n = n;
        while (n)
        p->val[--n] = 0.0;
        } else {
        free(p);
        p = NULL;
        }
        }
        return p;
        }

        void vec_free(struct vector *p) {
        free(p->val); /* or perhaps check p!=NULL first */
        free(p);
        }

        The rest of the code remains unchanged.
        [color=blue]
        >So, I can't really see the benefit of using empty array notation here.[/color]

        The advantage to using the F.A.M. (or the struct hack) is that we
        avoid the second malloc() call and the associated extra source code,
        and the underlying machine code tends to be more efficient as well
        (although the latter is certainly never promised).
        [color=blue]
        >And anyway, how would you malloc some memory for element when it's an
        >array type ...[/color]

        As shown above.
        [color=blue]
        >So, then I thought, well, maybe the notation would allow me to
        >initialise a struct ...[/color]

        There is a way to map the F.A.M. or struct hack onto an actual,
        initialized instance. This is at least "technicall y iffy", however,
        as there is no guarantee that the offets of the members of the
        "inflexible " structure match those of the "flexible" structure.
        [color=blue]
        >struct foo
        >{
        > int a;
        > char b[];
        >};
        >
        >struct foo s = {10, {'f', 'o', 'o'}};[/color]

        You cannot do this; but you can do:

        struct foo {
        int a;
        char b[];
        };
        struct foo_with_b_size _3 {
        int a;
        char b[3];
        } s = { 10, { 'f', 'o', 'o' } };

        struct foo *get_s(void) {
        return (struct foo *)&s;
        }

        This runs into that "technicall y iffy" problem (although, again,
        it seems to work on all real implementations ). The fact that it
        requires a pointer cast is reason enough to be suspicious: any code
        that requires a pointer cast is probably skating on thin ice.
        --
        In-Real-Life: Chris Torek, Wind River Systems
        Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
        email: forget about it http://web.torek.net/torek/index.html
        Reading email is like searching for food in the garbage, thanks to spammers.

        Comment

        • Ben Hinkle

          #5
          Re: Flexible arrays - v confused


          [color=blue]
          > So, I can't really see the benefit of using empty array notation here.
          > And anyway, how would you malloc some memory for element when it's an
          > array type - surely p -> element = malloc... is an error, as element is
          > an array, and so can't be assigned to? That leaves me thinking that to
          > get it to work like that, maybe you should malloc a lump of memory,
          > then cast the address to a header *, access element, and then index
          > into that. But again, you could do this using straight forward pointer
          > notation in the declaration of element?[/color]

          The difference is that with a pointer the struct would have a data layout
          like
          int, elem*
          where the pointer would point to an array of elem. With flexible arrays the
          data layout looks like
          int, elem[0], elem[1], etc
          where you allocates the struct and the flexible array all in one block.
          Think of the flexible array as if there were a fixed size like elem
          element[10] and then allow the "10" to be defined at run time. The other
          reply gave an example malloc call.


          Comment

          • mechanicfem@googlemail.com

            #6
            Re: Flexible arrays - v confused


            Chris Torek wrote:[color=blue]
            > In article <1139922094.022 699.95880@g47g2 000cwa.googlegr oups.com>
            > <mechanicfem@go oglemail.com> wrote:[color=green]
            > >I thought (as they're in c99) that flexible arrays were there for a
            > >number of reasons ...[/color]
            >
            > They really exist for just one reason: to legitimize the old
            > "struct hack" (as it is called in the FAQ).
            >
            > To review for a moment, the "struct hack" is done something like this:
            >
            > #include <stdlib.h>
            >
            > struct vector {
            > size_t n; /* number of values in the vector */
            > double val[1]; /* actually size n */
            > };
            >
            > struct vector *vec_new(size_t n) {
            > struct vector *p;
            >
            > /* need n-1 here because the array has an "extra" element */
            > p if (p ! p->n while (n)
            > p->val[--n] }
            > return p;
            > }
            >
            > void vec_free(struct vector *p) {
            > free(p);
            > }
            >
            > double vec_access(stru ct vector *p, size_t i) {
            > if (i > panic("vec_acce ss: nonexistent element %lu\n", (unsigned long)i);
            > return p->val[i];
            > }
            >
            > To use a C99 "flexible array" structure member, we simply remove
            > the constant "1" and get rid of the code that subtracts 1 from the
            > computation passed to malloc(). This simplifies vec_new:
            >
            > p
            > and otherwise has no effect on the machine-level code produced,
            > except that it *must* actually work, whereas the pre-C99 version
            > was allowed to produce code that misbehaved at runtime. (That is,
            > the "struct hack" is not 100% legitimate, although no one has
            > produced examples of machines on which it does not work.)
            >[color=green]
            > >However, the same 'trick' can be done if [the struct that has the
            > >Flexible Array Member] contained [a pointer] ...[/color]
            >
            > In other words, we could equally write:
            >
            > struct vector {
            > size_t n;
            > double *val;
            > };
            >
            > struct vector *vec_new(size_t n) {
            > struct vector *p;
            >
            > p if (p ! p->val if (p->val ! p->n while (n)
            > p->val[--n] } else {
            > free(p);
            > p }
            > }
            > return p;
            > }
            >
            > void vec_free(struct vector *p) {
            > free(p->val); /* or perhaps check p!=NULL first */
            > free(p);
            > }
            >
            > The rest of the code remains unchanged.
            >[color=green]
            > >So, I can't really see the benefit of using empty array notation here.[/color]
            >
            > The advantage to using the F.A.M. (or the struct hack) is that we
            > avoid the second malloc() call and the associated extra source code,
            > and the underlying machine code tends to be more efficient as well
            > (although the latter is certainly never promised).
            >[color=green]
            > >And anyway, how would you malloc some memory for element when it's an
            > >array type ...[/color]
            >
            > As shown above.
            >[color=green]
            > >So, then I thought, well, maybe the notation would allow me to
            > >initialise a struct ...[/color]
            >
            > There is a way to map the F.A.M. or struct hack onto an actual,
            > initialized instance. This is at least "technicall y iffy", however,
            > as there is no guarantee that the offets of the members of the
            > "inflexible " structure match those of the "flexible" structure.
            >[color=green]
            > >struct foo
            > >{
            > > int a;
            > > char b[];
            > >};
            > >
            > >struct foo s[/color]
            > You cannot do this; but you can do:
            >
            > struct foo {
            > int a;
            > char b[];
            > };
            > struct foo_with_b_size _3 {
            > int a;
            > char b[3];
            > } s
            > struct foo *get_s(void) {
            > return (struct foo *)&s;
            > }
            >
            > This runs into that "technicall y iffy" problem (although, again,
            > it seems to work on all real implementations ). The fact that it
            > requires a pointer cast is reason enough to be suspicious: any code
            > that requires a pointer cast is probably skating on thin ice.
            > --[/color]

            Thanks to all that replied. The veil has been lifted.

            However, couple of questions about the code.

            If you simply knocked this up to show the methods etc, what follows is
            probably not too interesting, or worth commenting upon. However, there
            was a couple of things that made me think, so I include them here to
            see if there's any comment etc.

            Although sizeof never executes its 'arg' (don't know if those are the
            right terms, but I'm sure they're good enough to be understood), I'd
            personally go with
            sizeof(vector) --rather than-- sizeof *p

            To perhaps calm the nerves of any maintenence programmer that see an
            auto *p that *seems* to be /dereferenced/ ?

            The former, of course, would need a typedef struct vector vector -
            which I also think would make the code a little easier to read.

            On vec_access, why not return a double * to allow both read and write
            access, e.g., using return &(p->val[i]);

            x

            Jo

            Comment

            • Alex Fraser

              #7
              Re: Flexible arrays - v confused

              <mechanicfem@go oglemail.com> wrote in message
              news:1140006232 .868066.103640@ g44g2000cwa.goo glegroups.com.. .
              [snip][color=blue]
              > Although sizeof never executes its 'arg' (don't know if those are the
              > right terms, but I'm sure they're good enough to be understood),[/color]

              I think "evaluates" and "operand" are the terms you are looking for.
              [color=blue]
              > I'd personally go with
              > sizeof(vector) --rather than-- sizeof *p
              >
              > To perhaps calm the nerves of any maintenence programmer that see an
              > auto *p that *seems* to be /dereferenced/ ?[/color]

              If, perhaps as a maintenance programmer, I see the following line of code:

              p = malloc(sizeof *p);

              I immediately know that the call requests allocation of the right amount of
              space for one of whatever p points to.

              OTOH, if I see something like:

              p = malloc(sizeof (struct vector));

              I first have to remember or check that p has the correct type.

              Alex


              Comment

              • mechanicfem@googlemail.com

                #8
                Re: Flexible arrays - v confused


                Alex Fraser wrote:[color=blue]
                > <mechanicfem@go oglemail.com> wrote in message
                > news:1140006232 .868066.103640@ g44g2000cwa.goo glegroups.com.. .
                > [snip][color=green]
                > > Although sizeof never executes its 'arg' (don't know if those are the
                > > right terms, but I'm sure they're good enough to be understood),[/color]
                >
                > I think "evaluates" and "operand" are the terms you are looking for.
                >[color=green]
                > > I'd personally go with
                > > sizeof(vector) --rather than-- sizeof *p
                > >
                > > To perhaps calm the nerves of any maintenence programmer that see an
                > > auto *p that *seems* to be /dereferenced/ ?[/color]
                >
                > If, perhaps as a maintenance programmer, I see the following line of code:
                >
                > p = malloc(sizeof *p);
                >
                > I immediately know that the call requests allocation of the right amount of
                > space for one of whatever p points to.
                >
                > OTOH, if I see something like:
                >
                > p = malloc(sizeof (struct vector));
                >
                > I first have to remember or check that p has the correct type.[/color]

                Hmmm, yes, that's a good observation, and reason for doing it. Thanks.

                x

                Jo

                Comment

                • Keith Thompson

                  #9
                  Re: Flexible arrays - v confused

                  mechanicfem@goo glemail.com writes:
                  [...][color=blue]
                  > Although sizeof never executes its 'arg' (don't know if those are the
                  > right terms, but I'm sure they're good enough to be understood), I'd
                  > personally go with
                  > sizeof(vector) --rather than-- sizeof *p
                  >
                  > To perhaps calm the nerves of any maintenence programmer that see an
                  > auto *p that *seems* to be /dereferenced/ ?[/color]

                  The operand of the sizeof operator is not evaluated *unless* the
                  operand's type is a variable length array (which isn't the case here).

                  Somebody else explained the reason for using sizeof *p, but I'll
                  expand on it a little.

                  Given:

                  some_type *ptr;
                  ...
                  ptr = malloc(sizeof(s ome_type));

                  it's very easy to change the declaration to

                  some_other_type *ptr;

                  and forget to change the argument to the malloc() call. If you use
                  the recommended form:

                  ptr = malloc(sizeof *ptr);

                  that's not an issue.
                  [color=blue]
                  > The former, of course, would need a typedef struct vector vector -
                  > which I also think would make the code a little easier to read.[/color]

                  No, a typedef isn't needed:

                  struct vector *ptr;
                  ptr = malloc(sizeof struct vector);

                  is perfectly legal. (And in my opinion, routinely using typedefs for
                  all structs is not useful; it creates two distinct names for each type
                  when one name, "struct vector" is quite sufficient.)

                  --
                  Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                  San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
                  We must do something. This is something. Therefore, we must do this.

                  Comment

                  • Old Wolf

                    #10
                    Re: Flexible arrays - v confused

                    Keith Thompson wrote:[color=blue]
                    >
                    > struct vector *ptr;
                    > ptr = malloc(sizeof struct vector);
                    >
                    > is perfectly legal.[/color]

                    sizeof(struct vector)

                    When you are in the habit of omitting the brackets for "sizeof var",
                    it's easy to forget to include them for "sizeof(typenam e)" .

                    Comment

                    • Keith Thompson

                      #11
                      Re: Flexible arrays - v confused

                      "Old Wolf" <oldwolf@inspir e.net.nz> writes:[color=blue]
                      > Keith Thompson wrote:[color=green]
                      >>
                      >> struct vector *ptr;
                      >> ptr = malloc(sizeof struct vector);
                      >>
                      >> is perfectly legal.[/color]
                      >
                      > sizeof(struct vector)
                      >
                      > When you are in the habit of omitting the brackets for "sizeof var",
                      > it's easy to forget to include them for "sizeof(typenam e)" .[/color]

                      Absolutely correct; thanks for catching my error.

                      --
                      Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                      San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
                      We must do something. This is something. Therefore, we must do this.

                      Comment

                      • Joe Wright

                        #12
                        Re: Flexible arrays - v confused

                        Keith Thompson wrote:[color=blue]
                        > "Old Wolf" <oldwolf@inspir e.net.nz> writes:
                        >[color=green]
                        >>Keith Thompson wrote:
                        >>[color=darkred]
                        >>> struct vector *ptr;
                        >>> ptr = malloc(sizeof struct vector);
                        >>>
                        >>>is perfectly legal.[/color]
                        >>
                        >> sizeof(struct vector)
                        >>
                        >>When you are in the habit of omitting the brackets for "sizeof var",
                        >>it's easy to forget to include them for "sizeof(typenam e)" .[/color]
                        >
                        >
                        > Absolutely correct; thanks for catching my error.
                        >[/color]
                        Also it is interesting to name things consistently. At my house..

                        parens (parentheses) look like ()
                        brackets look like [] and
                        braces look like {}

                        I use space in 'sizeof (typename)' so that sizeof operator cannot so
                        easily be confused with a function.

                        --
                        Joe Wright
                        "Everything should be made as simple as possible, but not simpler."
                        --- Albert Einstein ---

                        Comment

                        • Keith Thompson

                          #13
                          Re: Flexible arrays - v confused

                          Joe Wright <joewwright@com cast.net> writes:[color=blue]
                          > Keith Thompson wrote:[color=green]
                          >> "Old Wolf" <oldwolf@inspir e.net.nz> writes:[color=darkred]
                          >>>Keith Thompson wrote:
                          >>>
                          >>>> struct vector *ptr;
                          >>>> ptr = malloc(sizeof struct vector);
                          >>>>
                          >>>>is perfectly legal.
                          >>>
                          >>> sizeof(struct vector)
                          >>>
                          >>>When you are in the habit of omitting the brackets for "sizeof var",
                          >>>it's easy to forget to include them for "sizeof(typenam e)" .[/color]
                          >> Absolutely correct; thanks for catching my error.
                          >>[/color]
                          > Also it is interesting to name things consistently. At my house..
                          >
                          > parens (parentheses) look like ()
                          > brackets look like [] and
                          > braces look like {}[/color]

                          That's what I call them too, but I think common usage differs outside
                          the US. When it doubt, show the actual characters.
                          [color=blue]
                          > I use space in 'sizeof (typename)' so that sizeof operator cannot so
                          > easily be confused with a function.[/color]

                          That's a good idea.

                          --
                          Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                          San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
                          We must do something. This is something. Therefore, we must do this.

                          Comment

                          • websnarf@gmail.com

                            #14
                            Re: Flexible arrays - v confused

                            Chris Torek wrote:[color=blue]
                            > #include <stdlib.h>
                            > struct vector {
                            > size_t n; /* number of values in the vector */
                            > double val[1]; /* actually size n */
                            > };
                            >
                            > struct vector *vec_new(size_t n) {
                            > struct vector *p;
                            >
                            > /* need n-1 here because the array has an "extra" element */
                            > p = malloc(sizeof *p + (n ? n - 1 : 0) * sizeof p->val[0]);[/color]

                            I do this as follows:

                            p = (struct vector *) malloc (offsetof (struct vector, val) +
                            n * sizeof (p->val[0]));

                            Which means I need a #include <stddef.h>

                            --
                            Paul Hsieh
                            Pobox has been discontinued as a separate service, and all existing customers moved to the Fastmail platform.



                            Comment

                            • Keith Thompson

                              #15
                              Re: Flexible arrays - v confused

                              websnarf@gmail. com writes:[color=blue]
                              > Chris Torek wrote:[color=green]
                              >> #include <stdlib.h>
                              >> struct vector {
                              >> size_t n; /* number of values in the vector */
                              >> double val[1]; /* actually size n */
                              >> };
                              >>
                              >> struct vector *vec_new(size_t n) {
                              >> struct vector *p;
                              >>
                              >> /* need n-1 here because the array has an "extra" element */
                              >> p = malloc(sizeof *p + (n ? n - 1 : 0) * sizeof p->val[0]);[/color]
                              >
                              > I do this as follows:
                              >
                              > p = (struct vector *) malloc (offsetof (struct vector, val) +
                              > n * sizeof (p->val[0]));
                              >
                              > Which means I need a #include <stddef.h>[/color]

                              Why do you uselessly cast the result of malloc()?

                              --
                              Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                              San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
                              We must do something. This is something. Therefore, we must do this.

                              Comment

                              Working...