简体   繁体   中英

ANSI C / C89 struct to int array[]

Is there an efficient way to assign/convert a struct to array and vice-versa?

The struct I have is as follows:

struct A {
    int x, y;
}

struct B {
    struct A start;
    struct A end;
}

Basically they contain xy coordinates for start and end positions.

I need to assign them efficiently however currently I can only do this

/* sample code */
struct B b;
b.start.x = arr[i];
b.start.y = arr[i];
b.end.x = arr[i];
b.end.y = arr[i];


/* I can't do this in ANSI C / C89 as compound literals only allow constants */
b = (struct B) {(struct A) {arr[0], arr[1]}, (struct A) {arr[2], arr[3]}};

I can use compound literals as well but it gives me a warning in gcc when I compile with flags -Wall -pedantic -ansi

Is there a way to reduce those 4 lines of assignment to just one without getting a warning with the flags mentioned above.

Regards

Edit: fixed compound literal syntax

struct A
{
    int x, y;
};

struct B
{
    struct A start;
    struct A end;
};

void InitA(struct A* s, int x, int y)
{
    s->x = x;
    s->y = y;
}

void InitB(struct B* s, int x1, int y1, int x2, int y2)
{
    InitA(&s->start, x1, y1);
    InitA(&s->end, x2, y2);
}

void InitBFromArray(struct B* s, int *a)
{
    InitB(s, a[0], a[1], a[2], a[3]);
}

int main()
{
    int a[] = { 1, 2, 3, 4 };
    struct B s;
    InitBFromArray(&s, a);
}

I you want to do it efficiently (the first code and the second code are the same efficiency in a good compiler, as you are doing the same) just write:

int arr[N], i = 0;

b.start.x = arr[i++];
b.start.y = arr[i++];
b.end.x = arr[i++];
b.end.y = arr[i++];

this has the same effect as both of the examples you used. Also, you can do this (but the majority of compilers should generate the same code:

int arr[N], *p = arr;

b.start.x = *p++;
b.start.y = *p++;
b.end.x = *p++;
b.end.y = *p++;

but a good compiler must generate the same code as the above example.

Why do you think your first approach is going to be less efficient than the second?

More efficiency can be obtained, by copying memory blocks (or even reinterpreting your data with a different pointer) but that's definitively non portable code

struct B *ptr = (struct B *)arr;  /* or arr + 4*i */
/* now use ptr->start.x, ptr->start.y, ptr->end.x and ptr->end.y */

Note that this last example is probably the most efficient one, in that you don't have to copy anything, but is definitively nonportable , incorrect and not recommended coding. (and sometimes work!)

Assuming that you meant to write something similar to:

struct B b;
b.start.x = arr[i++];
b.start.y = arr[i++];
b.end.x = arr[i++];
b.end.y = arr[i++];

I would go with the four-line assigments rather than the compound literal, because the latter appears too convoluted to be useful. If I need to do these assignments more than once and a loop isn't feasible, then I would write a helper function or macro. Eg:

function init_b (struct B *b, int *a)
{
  b->start.x = a[0];
  b->start.y = a[1];
  b->end.x = a[2];
  b->end.y = a[3];
}

init_b (&b, arr);

/* or */

#define INIT_B(b,a) do {\
  (b).start.x = (a)[0];\
  (b).start.y = (a)[1];\
  (b).end.x = (a)[2];\
  (b).end.y = (a)[3];\
} while (0)

INIT_B (b, arr);

I wouldn't be surprised if a compiler, with optimizations enabled, generated almost exactly the same code for all of the above.

The do...while(0) construct ensures that the macro works correctly when used as a single statement body of a for loop, or similar:

struct B *b;
/* ... */
int i;
for (i = 0; i < /*...*/; i++)
  INIT_B (b[i], arr + 4*i);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM