Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Flexible arrays - v confused

Reply
Thread Tools

Flexible arrays - v confused

 
 
mechanicfem@googlemail.com
Guest
Posts: n/a
 
      02-14-2006
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

 
Reply With Quote
 
 
 
 
Zero
Guest
Posts: n/a
 
      02-14-2006
#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.

 
Reply With Quote
 
 
 
 
Default User
Guest
Posts: n/a
 
      02-14-2006
Zero wrote:


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


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.
 
Reply With Quote
 
Chris Torek
Guest
Posts: n/a
 
      02-14-2006
In article <(E-Mail Removed). com>
<(E-Mail Removed)> wrote:
>I thought (as they're in c99) that flexible arrays were there for a
>number of reasons ...


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(struct vector *p, size_t i) {
if (i >= p->n)
panic("vec_access: 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.)

>However, the same 'trick' can be done if [the struct that has the
>Flexible Array Member] contained [a pointer] ...


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.

>So, I can't really see the benefit of using empty array notation here.


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).

>And anyway, how would you malloc some memory for element when it's an
>array type ...


As shown above.

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


There is a way to map the F.A.M. or struct hack onto an actual,
initialized instance. This is at least "technically iffy", however,
as there is no guarantee that the offets of the members of the
"inflexible" structure match those of the "flexible" structure.

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


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 "technically 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 (4039.22'N, 11150.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.
 
Reply With Quote
 
Ben Hinkle
Guest
Posts: n/a
 
      02-14-2006


> 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?


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.


 
Reply With Quote
 
mechanicfem@googlemail.com
Guest
Posts: n/a
 
      02-15-2006

Chris Torek wrote:
> In article <(E-Mail Removed). com>
> <(E-Mail Removed)> wrote:
> >I thought (as they're in c99) that flexible arrays were there for a
> >number of reasons ...

>
> 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(struct vector *p, size_t i) {
> if (i > panic("vec_access: 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.)
>
> >However, the same 'trick' can be done if [the struct that has the
> >Flexible Array Member] contained [a pointer] ...

>
> 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.
>
> >So, I can't really see the benefit of using empty array notation here.

>
> 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).
>
> >And anyway, how would you malloc some memory for element when it's an
> >array type ...

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

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

> 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 "technically 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.
> --


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

 
Reply With Quote
 
Alex Fraser
Guest
Posts: n/a
 
      02-15-2006
<(E-Mail Removed)> wrote in message
news:(E-Mail Removed) oups.com...
[snip]
> 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 think "evaluates" and "operand" are the terms you are looking for.

> 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/ ?


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


 
Reply With Quote
 
mechanicfem@googlemail.com
Guest
Posts: n/a
 
      02-15-2006

Alex Fraser wrote:
> <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed) oups.com...
> [snip]
> > 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 think "evaluates" and "operand" are the terms you are looking for.
>
> > 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/ ?

>
> 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.


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

x

Jo

 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      02-15-2006
http://www.velocityreviews.com/forums/(E-Mail Removed) writes:
[...]
> 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 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(some_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.

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


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_Keith) (E-Mail Removed) <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.
 
Reply With Quote
 
Old Wolf
Guest
Posts: n/a
 
      02-15-2006
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(typename)" .

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Flexible arrays getting in the way? Andrea Taverna C Programming 4 04-05-2009 12:39 PM
Multidimensional arrays and arrays of arrays Philipp Java 21 01-20-2009 08:33 AM
confused between char and char* and connection to Arrays arnuld C++ 19 03-30-2007 04:42 AM
confused about arrays pruebauno@latinmail.com C++ 2 10-07-2005 05:46 PM
Compiler error occurred when try to use a flexible template expression in preprocessor definesCompiler error occurred when try to use a flexible template expression in preprocessor defines snnn C++ 6 03-14-2005 04:09 PM



Advertisments