【问题标题】:iterating through a structure in C遍历C中的结构
【发布时间】:2020-05-13 00:10:48
【问题描述】:

我正在从事一个项目,该项目涉及通过单链表实现 Stack 数据结构。更具体地说,我想知道是否有一种方法可以通过 struct 的属性自动循环(迭代),这些属性恰好是不同的数据类型——这在读取输入时会有很大帮助,或者在添加更多属性时,我不必手动更改所有内容。

具体结构

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

例如:attribute[0]regNumberattribute[1]fNameattribute[n] 是 n^{th} 元素

【问题讨论】:

  • @Rashik 我知道这个解决方案,但它是关于相同数据类型的属性,在我的情况下,我的一些数据类型属于不同类型。我知道我可以将它们全部设为char,但我想知道是否还有其他解决方法。
  • 您可以使用更复杂的数据类型,在其中存储指向学生结构的每个元素的指针并遍历它。它适用于任何元素数据大小,但需要为指向数据的指针提供额外的存储空间,并且需要适当的构造。它还会有知道要转换到和从什么类型的指针的问题,因此它需要一些帮助。如果有所有 char * ,它可能会工作(一个 char * 锯齿状数组)。但实际上,您这样做是在反对该语言。 C 不反映。
  • 你不能在 C 中做到这一点。如果你所做的任何事情都依赖于这样的东西来工作,那么你很可能是以错误的方式思考它。
  • 您需要的是反射功能。看看这里:github.com/loganek/mkcreflect

标签: c struct


【解决方案1】:

我想不出任何使用未定义行为或非常奇怪的构造的好方法。而且你想要不同类型的字段这一事实并没有让它变得更容易。

如果我想编写这样的代码(我不想),我可能会这样做。

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;
}

然后你可以像这样使用它:

int main()
{
    struct Student s = { "xxx333", "Jonny", "BGood", "my@email.com", 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)
        );
}

自然而然地,我没有看到绕过这些演员的好方法。一种方法,但不一定是好方法,是这样做:

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", "my@email.com", 12345, 22 };

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

请注意,我有时使用结构,有时使用指向结构的指针作为函数的参数。选择不是出于任何特殊原因。它只是碰巧是这样的。你可以做任何一种方式,两者都有其优点和缺点。如果性能是一个问题,我会寻求指针。工会也是一样。我本可以选择一个 char 数组并改用 strncpy。我本可以跳过 int 和 short 的指针。在这里,我的想法是,如果所有工会成员都是指针,那就更清楚了。但是你必须对这一切做出你自己的决定。如果您使用指针,在适当的情况下使用 const 限定符可能是明智之举。

如果你真的想这样做,我想你可以这样做:

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

这可以与上述技术结合使用。比如

struct attr attribs[6];
for(int i=0; i<6; i++)
    attribs[i] = get_attr2(&s, i);

【讨论】:

  • 如果没有额外的反射细节,我们无法知道如何处理这些void* 指针并正确使用它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
  • 2011-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
相关资源
最近更新 更多