Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iterating through a structure in C

Tags:

c

struct

I am working on a project that involves the implementation of the Stack data structure via a singly linked list. More specifically, I am wondering if there is a way to automatically cycle(iterate) through the attributes of a struct which happen to be of different data types -- it would greatly help when reading input, or when adding more attributes, so, I don't have to manually change everything.

Specific struct

typedef struct Student
{
    char regNumber[30];
    char fName[30];
    char lName[30];
    char email[50];
    int phoneNumber;
    short age;
} Student;

For example: attribute[0] would be regNumber, attribute[1] would be fName, attribute[n] would be the n^{th} element

like image 981
Alexandros Voliotis Avatar asked Oct 18 '25 08:10

Alexandros Voliotis


1 Answers

I cannot think of any good way to do this that neither uses undefined behavior, nor very weird constructs. And the fact that you want fields of different type does not make it easier.

If I wanted to write code like this (which I don't) I would probably do something like this.

void *get_attr(struct Student *student, int field)
{
    switch(field) {
    case 0 : return (void*)&student->regNumber;
    case 1 : return (void*)&student->fName;
    case 2 : return (void*)&student->lName;
    case 3 : return (void*)&student->email;
    case 4 : return (void*)&student->phoneNumber;
    case 5 : return (void*)&student->age;
    }
    return NULL;
}

and then you can use it like this:

int main()
{
    struct Student s = { "xxx333", "Jonny", "BGood", "[email protected]", 12345, 22 };
    printf ("%s\n%s\n%s\n%s\n%d\n%d\n",
            (char*)get_attr(&s, 0),
            (char*)get_attr(&s, 1),
            (char*)get_attr(&s, 2),
            (char*)get_attr(&s, 3),
            *(int*)get_attr(&s, 4),
            *(short*)get_attr(&s, 5)
        );
}

Spontaneously, I don't see a good way around those casts. One way, but not necessarily a good way, is to do something like this:

union attr_field {
    char *c;
    int *i;
    short *s;
};

enum attr_type { CHAR, INT, SHORT };

struct attr {
    union attr_field attr;
    enum attr_type type;
};

struct attr get_attr2(struct Student *student, int field)
{
    struct attr ret;
    switch(field) {
    case 0 : ret.attr.c = student->regNumber; ret.type = CHAR; break;
    case 1 : ret.attr.c = student->fName; ret.type = CHAR; break;
    case 2 : ret.attr.c = student->lName; ret.type = CHAR; break;
    case 3 : ret.attr.c = student->email; ret.type = CHAR; break;
    case 4 : ret.attr.i = &student->phoneNumber; ret.type = INT; break;
    case 5 : ret.attr.s = &student->age; ret.type = SHORT; break;
    }
    return ret;
}

void print_attr(struct attr a)
{
    switch(a.type) {
    case CHAR: printf("%s\n", a.attr.c); break;
    case INT: printf("%d\n", *a.attr.i); break;
    case SHORT: printf("%d\n", *a.attr.s); break;
    }
}

int main()
{
    struct Student s = { "xxx333", "Jonny", "BGood", "[email protected]", 12345, 22 };

    for(int i=0; i<6; i++) {
        struct attr a = get_attr2(&s, i);
        print_attr(a);
    }
}

Note that I sometimes used struct and sometimes pointer to struct as argument to functions. The choice was not due to any particular reason. It just happened to be that way. You can do it either way, and both have their pros and cons. If performance is an issue, I'd go for pointers. Same thing with the union. I could have chosen a char array and used strncpy instead. And I could have skipped the pointers for int and short. Here my thought was something like that it's more clear if ALL union members are pointers. But you have to make your own decisions about all this. If you go for pointers, it might be wise to use the const qualifier where appropriate.

If you really want to do so, I guess you could do something like this:

void *attribs[6];
attribs[0] = (void*)s.regNumber;
printf("%s", (char*)attribs[0]);

That could be combined with the techniques mentioned above. For instance

struct attr attribs[6];
for(int i=0; i<6; i++)
    attribs[i] = get_attr2(&s, i);
like image 155
klutt Avatar answered Oct 19 '25 23:10

klutt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!