Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over same type struct members in C

Is it possible to iterate of a C struct, where all members are of same type, using a pointer. Here's some sample code that does not compile:

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

typedef struct
{
    int mem1 ;
    int mem2 ;
    int mem3 ;
    int mem4 ;
} foo ;

void my_func( foo* data )
{
    int i ;
    int* tmp = data ; // This line is the problem

    for( i = 0; i < 4; ++i )
    {
        ++tmp ;
        printf( "%d\n", *tmp ) ;
    }
}

int main()
{
    foo my_foo ;
    //
    my_foo.mem1 = 0 ;
    my_foo.mem2 = 1 ;
    my_foo.mem3 = 2 ;
    my_foo.mem4 = 3 ;
    //
    my_func( &my_foo ) ;
    return 0 ;
}

The members of foo should be aligned in memory to be one after another, assuming your compiler/kernel does not try to provide stack protection for buffer overflow.

So my question is:

How would I iterate over members of a C struct that are of the same type.

like image 931
Misha M Avatar asked Dec 08 '09 20:12

Misha M


4 Answers

Most of the attempts using a union with an array are prone to failure. They stand a decent chance of working as long as you only use int's, but for other, especially smaller, types, they're likely to fail fairly frequently because the compiler can (and especially with smaller types often will) add padding between members of a struct, but is not allowed to do so with elements of an array).

C does, however, have an offsetof() macro that you can use. It yields the offset of an item in a struct, so you can create an array of offsets, then (with a bit of care in casting) you can add that offset to the address of the struct to get the address of the member. The care in casting is because the offset is in bytes, so you need to cast the address of the struct to char *, then add the offset, then cast the result to the type of the member (int in your case).

like image 95
Jerry Coffin Avatar answered Nov 15 '22 01:11

Jerry Coffin


From the language point of view: you can't. data members of the struct are not... er.. "iteratible" in C, regardless of whether they are of the same type or of different types.

Use an array instead of a bunch of independent members.

like image 32
AnT Avatar answered Nov 15 '22 01:11

AnT


The easiest way would be to create a union, one part which contains each member individually and one part which contains an array. I'm not sure if platform-dependent padding might interfere with the alignment.

like image 23
Mark Ransom Avatar answered Nov 15 '22 02:11

Mark Ransom


You can also use an unnamed union/struct:

struct foo {
    union {
        struct {
            int mem1;
            int mem2;
            int mem3;
            int mem4;
        };
        int elements[4];
    };
};

foo thefoo;
for (int i = 0; i < 4; ++i) {
    thefoo.elements[i] = i;
}

This might not work on some compilers, int this case you'll have to explicitily name the union and struct inside foo,

like image 2
Andreas Brinck Avatar answered Nov 15 '22 01:11

Andreas Brinck