sacheie sacheie - 1 month ago 10
C Question

What's the right way to update an array of nested C structs?

I'm having trouble finding the right way to mutate members of an array of nested structs in C99 (after passing the array to another function). The array is declared within main() - so it's on the stack, I presume? - and, I may as well just show the relevant parts of the code.

typedef struct
{
uint32_t x, y;
} point;

typedef struct
{
uint32_t r, g, b, a;
} rgba;

typedef struct
{
point a, b, c;
rgba p;
} tri;


Those are the structs; here's how I create a random triangle (an ability I need in other parts of the program):

tri triangle()
{
tri t;

t.a.x = rand() % xres;
t.a.y = rand() % yres;
t.b.x = rand() % xres;
t.b.y = rand() % yres;
t.c.x = rand() % xres;
t.c.y = rand() % yres;
t.p.r = rand() % 256;
t.p.g = rand() % 256;
t.p.b = rand() % 256;
t.p.a = rand() % 256;

return t;
}


And here's the part (some lines you can ignore) where I declare and populate the array:

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

uint32_t img[xres][yres];

copyInputImage(img);

srand(time(NULL));

tri trigons[NUM_TRIGONS];

for (int i = 0; i < NUM_TRIGONS; i++)
{
trigons[i] = triangle();
}


All that successfully gets me an array of random triangles.

Now here's my problem: depending on the program parameters, sometimes I want to immediately re-populate the array with data from a text file. The text format is simple: one unsigned char on every line. Here's an example of what the data for a single pixel looks like in that file:

0
40
0
5
37
31
134
64
167
100


That's an example of the 10 primitives a triangle struct ultimately needs. All number strings are 3 digits or less, but my loop with fgets() involves a 5-char buffer to allow for the line terminator and the null terminator.

The function that does that looks like this:

void read_SVG(char* filename, tri* ts)
{
FILE* fp = fopen(filename, "r");

char str[5];

for (int i = 0; i < NUM_TRIGONS; i++)
{
tri *t = &(ts[i]);
point *a = &(ts[i]).a;
point *b = &(ts[i]).b;
point *c = &(ts[i]).c;
rgba *p = &(ts[i]).p;

while (fgets(str, 5, fp) != NULL)
{
a->x = (uint32_t) strtol(str, NULL, 10);
a->y = (uint32_t) strtol(str, NULL, 10);
b->x = (uint32_t) strtol(str, NULL, 10);
b->y = (uint32_t) strtol(str, NULL, 10);
c->x = (uint32_t) strtol(str, NULL, 10);
c->y = (uint32_t) strtol(str, NULL, 10);
p->r = (uint32_t) strtol(str, NULL, 10);
p->g = (uint32_t) strtol(str, NULL, 10);
p->b = (uint32_t) strtol(str, NULL, 10);
p->a = (uint32_t) strtol(str, NULL, 10);
}
t->a = *a;
t->b = *b;
t->c = *c;
t->p = *p;
}
fclose(fp);
}


And finally, this is how I invoke read_SVG(), shown within its context in main().

if (argc > 2)
{
read_SVG(argv[2], trigons);

draw(screen, trigons);
SDL_Flip(screen);
SDL_SaveBMP(screen, "wtf.bmp");
exit(0);
}


When I output the array from within this read_SVG(), it looks like the data I expected. But when I examine it back in main(), it looks like the random values that I'd initially populated.

So it seems the initial array was never updated at all. What have I done wrong, and can anyone suggest an overall better way of handling this type of nested struct pass and update problem?

I'm sure many questions about this have been asked; I can only hope mine will be comprehensive enough to help out some other C newbies. I tried a wide variety of approaches before coming here for help...

Answer

This part is redundant, since a,b,c, and p already points to the same memory that t points to:

    t->a = *a;
    t->b = *b;
    t->c = *c;
    t->p = *p;

But that's just a nitpick. ;) The important comment is probably that fgets() will read all file contents for the first item, and read nothing for the rest of the items. Maybe s/while/if/?

Edit to clarify: Here's an alternative version of the read_SVG() function:

void read_SVG(char* filename, tri* ts)
{
    FILE* fp = fopen(filename, "r");
    char str[5];
    int i = 0;

    while (i < NUM_TRIGONS && fgets(str, 5, fp) != NULL) {
        uint32_t val = (uint32_t) strtol(str, NULL, 10);
        ts[i].a.x = ts[i].a.y = 
        ts[i].b.x = ts[i].b.y = 
        ts[i].c.x = ts[i].c.y = 
        ts[i].p.r = ts[i].p.g = ts[i].p.b = ts[i].p.a = val;

        i++;
    }
    fclose(fp);
}
Comments