如何通过struct.variableWithNameOfTheProperty访问结构体属性?

xj3cbfub  于 2023-01-12  发布在  其他
关注(0)|答案(3)|浏览(143)

我是C语言的新手,我尝试使用“for”将数据插入到结构体中,如下所示:

char *personAttributes[6] = {"cpf", "name", "sex", "dtBorn", "city", "UF"};
    int i;

    Person pw;

    typedef struct 
    {
        char cpf;
        char name;
        char sex;
        int dtBorn;
        char city;
        char UF;
    } Person;

    for (i = 0; i < 6; i++)
    {
        printf("Input person's %s: \n", personAttributes[i]);
        scanf("%[^\n]", &pw.personAttributes[i]);
    }

而不是在多行中阅读每个结构属性(结构的每个元素一行,如:...,&pw.cpf,&pw.name...),我想动态地完成它,通过在for中传递参数,并通过数组中的索引访问它。
我试着这样做:

scanf("%[^\n]", &pw.personAttributes[i]);

但是这种访问方式显然不起作用。2有没有什么方法可以在C语言中做类似的事情?

rkttyhzu

rkttyhzu1#

(edit:其他海报让我想起了让这变得人性化得多的offsetof macro)
C不会自动知道如何去做,你希望你的程序在运行时知道Person的属性是什么(“cpf”、“name”等)。然而,当你用C编程时,你的程序并不知道这些。编译器知道调用了什么,但它不会将这些信息存储在编译后的程序中。在Python这样的动态语言中,编译器和你的程序之间的分离就更少了,你可以做这样的事情,但是在C语言中,你写的就是你得到的。
然而,这仍然可以通过一些额外的工作来实现。你只需要显式地告诉你的程序它需要知道什么。你的程序需要知道每个属性的名称是什么,如何读取它,以及它在结构体中的存储位置。由于编译器不会自动将这些信息存储在你的程序中,你需要显式地将其存储在你的代码中,如下所示:

#include "stdio.h"
#include "stddef.h"
#include "stdint.h"

typedef struct 
{
    char cpf[80];
    char name[80];
    char Sex[80];
    int32_t dtBorn;
    char city[80];
    char UF[80];
} Person;

typedef struct
{
    char *label;  // What the attribute is called
    char *scanf_pattern;  // How to read it with scanf
    ptrdiff_t offset;  // Where in the struct the attribute is located
} AttributeInfo;

static const AttributeInfo attributes[] = {
    {"cpf", " %79[^\n]s", offsetof(Person, cpf)},
    {"name", " %79[^\n]s", offsetof(Person, name)},
    {"Sex", " %79[^\n]s", offsetof(Person, Sex)},
    {"dtBorn", "%d", offsetof(Person, dtBorn)},
    {"city", " %79[^\n]s", offsetof(Person, city)},
    {"UF", " %79[^\n]s", offsetof(Person, UF)}
};

现在,attributes常量包含了所有额外的信息,对于每个属性,它记住了属性名、属性的scanf字符串以及从Person结构体开始到属性所在位置的偏移量(这是offsetof宏为我们计算的;https://en.wikipedia.org/wiki/Offsetof有更多关于这方面的信息)。
现在,我们已经在attributes中保存了我们需要的额外信息。即使在运行时,我们的程序也有存储的信息来做你想做的事情。我们只需要显式地使用它,如下所示:

int main(int argc, char **argv) {
    Person pw;

    for (size_t i = 0; i < sizeof(attributes) / sizeof(AttributeInfo); i++)
    {
        AttributeInfo attribute = attributes[i];
        printf("Input person's %s: \n", attribute.label);
        int success = scanf(attribute.scanf_pattern, (void*) &pw + attribute.offset);
        if (success != 1) {
            printf("Not valid; try again.\n");
            scanf("%*[^\n]s");
            --i;
        }
    }
    printf("cpf: %s\n", pw.cpf);
    printf("name: %s\n", pw.name);
    printf("Sex: %s\n", pw.Sex);
    printf("dtBorn: %d\n", pw.dtBorn);
    printf("city: %s\n", pw.city);
    printf("UF: %s\n", pw.UF);
}

当我们编译并运行程序时,我们将得到:

$ gcc foo.c -o foo
...

$ ./foo
Input person's cpf: 
clam protection factor
Input person's name: 
Super Foonly
Input person's Sex: 
undecided
Input person's dtBorn: 
yesterday
Not valid; try again.
Input person's dtBorn: 
19700101
Input person's city: 
Anytown
Input person's UF: 
ulterior freshness
cpf: clam protection factor
name: Super Foonly
Sex: undecided
dtBorn: 19700101
city: Anytown
UF: ulterior freshness

您的问题很清楚,但您应该注意原始版本中存在的一些问题:

  • cpfname等都是单个字符,而不是字符串(字符数组)。我不知道“cpf”和“UF”是什么,但我假设您希望name、city和Sex是字符串。
  • 您的scanf格式没有宽度说明符(上面" %79[^\n]s"中的79)。这使得有人可以通过输入比您预期更长的字符串来破坏或利用您的程序。请查看http://www.crasseux.com/books/ctutorial/String-overflows-with-scanf.html以了解有关此问题的更多讨论。

我知道这些可能是出于你的目的和意图,但以防万一他们不是:

看起来你犯了每个人在第一次学习C语言时都会犯的错误。但是你想做的事情是可行的,也是合理的,我希望这能帮助你交流如何做。你可能想在花更多的时间思考指针数学之后再回到这个问题上来,这取决于你的舒适程度。但是不用担心!你很快就会学会的。

fumotvh3

fumotvh32#

没有办法使用字符串数组来迭代结构体的成员(因为成员的名称通常只在编译时可用)。你可以使用每个成员的(标识符)名称来获得指向每个成员的指针,但这不会保存你的任何努力。如果你需要迭代,请使用数组,或者你可以编写函数来访问值数组的正确位置。
另外,struct的一些成员类型为char(单个字母),可能应该是char *(字符串)。

bfnvny8b

bfnvny8b3#

没有简单的方法来实现这一点,C语言也没有对类似操作的直接支持。如果你用元素的名称,offsetof()sizeof每个成员的名称构建一个表,你可以模糊地接近它。(offsetof宏在<stddef.h>中定义。)但是存在如何编码每个成员的类型的问题,你可以编写适合于你的结构所使用的类型的代码,但是很难使它完全通用--因为结构可以是任意多样的。
看看这两个问题,寻找一些想法-但没有简单的方法来处理它:

C23将引入两个新的关键字-typeoftypeof_unqual(参见p114上的N3054 §6.7.2.5 Typeof specifiers)。现在还不清楚它们是否可以用于支持这一点。它们是语言中的类型说明符,在预处理器完成很久之后才生效(所以我不认为您能够将结果转换为字符串;字符串化是一个预处理器操作,但typeof可能只能由主编译器而不是预处理器来计算)。

相关问题