Flexible array member and alignement

T

tobiasoed

Hello,
I want to do something similar to this:

#include <stdlib.h>

struct coord {
double val;
double err;
};

struct point{
double value;
char foo;
struct coord coords[];
};

struct expe{
struct point *pts;
int np;
int nc;
};

struct expe *create_expe(int nc, int np){
struct expe *expe_p;

/* check for NULL omitted for simplicity */

expe_p = malloc(sizeof *expe_p);
expe_p->pts = malloc(np * (sizeof *expe_p->pts
+ nc * sizeof *expe_p->pts->coords));

expe_p->np = np;
expe_p->nc = nc;

return expe_p;
}

struct point *point(struct expe *expe_p, int i) {
return (struct point *) (
(char *) expe_p->pts
+ i * (sizeof *expe_p->pts
+ expe_p->nc * sizeof *expe_p->pts->coords)
);
}

int main(void) {
struct expe *expe_p=create_expe(4,100);

point(expe_p,2)->value=3;
point(expe_p,2)->coords[1].val=41;
point(expe_p,2)->coords[1].err=34;

return 0;
}

It compiles wo. warning with gcc 4.1.0 and runs ok but I'm not
sure if the the char foo in points can mess up the alignement
requirements of elements of the pts array. Any language lawyer
out there?
Tobias.
 
M

Michael Mair

Hello,
I want to do something similar to this:

#include <stdlib.h>

struct coord {
double val;
double err;
};

struct point{
double value;
char foo;
struct coord coords[];
};

Note: C99 demands sizeof (struct point) == offsetof(struct foo, c)
for
struct foo {double value; char foo; struct coord c[1];};
but gcc 3.x chooses the size of struct point such that coords meets the
maximum alignment requirements.
In the case of double, this may be the same.
The gcc crowd holds this to be a defect in the C99 standard;
unfortunately, they removed the description of this particular issue
from the C99 status page but did not state how gcc 4.x behaves.
struct expe{
struct point *pts;
int np;
int nc;
};

struct expe *create_expe(int nc, int np){
struct expe *expe_p;

/* check for NULL omitted for simplicity */

expe_p = malloc(sizeof *expe_p);
expe_p->pts = malloc(np * (sizeof *expe_p->pts
+ nc * sizeof *expe_p->pts->coords));

Structures with flexible array member must not be elements of an array;
this is a constraint violation (c.f. C99, 6.7.2.1#2), i.e. we do not
need to consider np != 1.
Once again, due to your using double, this probably "works" as
"expected" but in the general case gives potentially rise to alignment
issues.
expe_p->np = np;
expe_p->nc = nc;

return expe_p;
}

struct point *point(struct expe *expe_p, int i) {
return (struct point *) (
(char *) expe_p->pts
+ i * (sizeof *expe_p->pts
+ expe_p->nc * sizeof *expe_p->pts->coords)
);
}

int main(void) {
struct expe *expe_p=create_expe(4,100);

point(expe_p,2)->value=3;
point(expe_p,2)->coords[1].val=41;
point(expe_p,2)->coords[1].err=34;

These accesses might fail.
return 0;
}

It compiles wo. warning with gcc 4.1.0 and runs ok but I'm not
sure if the the char foo in points can mess up the alignement
requirements of elements of the pts array.

A safe and correct solution is to declare
struct expe{
struct point **pts_p;
int np;
int nc;
};
where pts_p points to allocated storage equivalent to an array np
of struct point *. You have to allocate storage for the elements of
pts_p to point to separately.
This means more allocations but easier access; i.e.
struct expe *create_expe(int nc, int np){
struct expe *expe_p;
int i;

expe_p = malloc(sizeof *expe_p);
if (expe_p != NULL) {
expe_p->pts_p = malloc(np * sizeof *expe_p->pts_p);
if (expe_p->pts_p != NULL) {
for (i = 0; i < np; i++) {
expe_p->pts_p = malloc(sizeof *(expe_p->pts_p[0])
+ nc * sizeof expe_p->pts_p[0]->coords[0]));
if (expe_p->pts_p == NULL) {
break;
}
}
if (i < np) {
/* error handling omitted */
}
} else {
/* error handling omitted */
}
expe_p->np = np;
expe_p->nc = nc;
}

return expe_p;
}

struct point *point(struct expe *expe_p, int i) {
if (i >= 0 && i < expe_p->np)
return expe_p->pts_p;
else
return NULL;
}
or you can replace point(expe_p, i) by expe_p->pts_p.
The code above is untested.


Cheers
Michael
 
T

tobiasoed

Michael said:
Structures with flexible array member must not be elements of an array;
this is a constraint violation (c.f. C99, 6.7.2.1#2), i.e. we do not
need to consider np != 1.
Once again, due to your using double, this probably "works" as
"expected" but in the general case gives potentially rise to alignment
issues.

Thank for that clarification, it means it's hopless to do it in a
conforming
fashion. I managed to cook something up that works for any type but
requires the gcc __alignof__ extension. I'll just forget the idea.
A safe and correct solution is to declare
struct expe{
struct point **pts_p;
int np;
int nc;
};
where pts_p points to allocated storage equivalent to an array np
of struct point *. You have to allocate storage for the elements of
pts_p to point to separately.

Yeah, but no. Too many allocation, not 'local enough' instead I now do

struct point {
double val;
char foo;
};

struct expe{
struct expe *expe;
struct coord *coord;
int nc, np;
};

and malloc np expes and np*nc coord. It makes accessing the stuff
a bit ugly, but at least it's legal :)
Thanks again,
Tobias.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top