有没有办法在循环中打印struct成员而不命名C中的每个成员?

每次我想要打印或初始化结构时,我都必须通过每个成员使代码不能重复使用。 有没有办法在for,while或do while循环中执行此操作?

typedef struct Client { char* Name; char* Address; char* Password; char* Privilege; }Client; 

以下是使用offsetof()的示例。 代码不完整,但是假设您使用C99或C11编译器,那么编译的代码是什么。 代码并不是最小的 – 特别是val_integer()fmt_integer()函数。 您可以通过编写复杂表达式来避免每个变量中的baseptr变量。

它所基于的场景是“从文本文件中读取配置到结构中”。 文件中的行可以为空或包含注释( #到行尾)或表单中的配置参数:

 NAME_OF_PARAMETER value-for-parameter 

名称后跟空格和值。 不同的配置元素具有不同的类型。 代码读取行,将非注释行拆分为键(参数名称)和值,然后调用代码将值转换为键的正确类型。 在这个方案基于的配置文件中,有大约300个配置参数,不同的参数可以有不同的有效范围,默认值等等,所以类型集相当大,因此,这是足够好的用于说明目的。 “真实”方案比这要复杂得多,因为大约有30对validation和格式化function – 但是它取代了一个单独的function,超过6,000行,每个包含300个单位,每行20个,每个都有一个非常小的源文件。总共不到一千行,并且没有大于约20行的函数(所有大小的数字近似值)。

 #include  #include  /* #include "xxconfig.h" // would define struct XX_Config */ typedef struct XX_Config { int xx_version_major; int xx_version_minor; char *xx_product_name; int xx_max_size; int xx_min_size; double xx_ratio; /* And so on for several hundred configuration elements */ } XX_Config; typedef enum TypeCode { T_INT, T_DOUBLE, T_CHARPTR, T_CHARARR, /* ...other types as needed */ } TypeCode; typedef struct Descriptor Descriptor; typedef struct TypeInfo TypeInfo; typedef int (*Validator)(char *buffer, const Descriptor *descr, void *data); typedef int (*Formatter)(char *buffer, size_t buflen, const Descriptor *descr, void *data); struct TypeInfo { TypeCode type; Validator valid; Formatter format; }; struct Descriptor { TypeCode type; size_t offset; char *name; }; extern int val_integer(char *buffer, const Descriptor *descr, void *data); extern int val_double(char *buffer, const Descriptor *descr, void *data); extern int val_charptr(char *buffer, const Descriptor *descr, void *data); extern int fmt_integer(char *buffer, size_t buflen, const Descriptor *descr, void *data); extern int fmt_double(char *buffer, size_t buflen, const Descriptor *descr, void *data); extern int fmt_charptr(char *buffer, size_t buflen, const Descriptor *descr, void *data); extern void err_report(const char *fmt, ...); extern int read_config(const char *file, XX_Config *config); extern int print_config(FILE *fp, const XX_Config *config); /* Would be static - but they're not defined so they need to be extern */ extern int is_comment_line(char *line); extern Descriptor *lookup(char *key); extern int split(char *line, char **key, char **value); static TypeInfo info[] = { [T_CHARPTR] = { T_CHARPTR, val_charptr, fmt_charptr }, [T_DOUBLE] = { T_DOUBLE, val_double, fmt_double }, [T_INT] = { T_INT, val_integer, fmt_integer }, // ...other types as needed }; static Descriptor xx_config[] = { { T_INT, offsetof(XX_Config, xx_version_major), "xx_version_major" }, { T_INT, offsetof(XX_Config, xx_version_minor), "xx_version_minor" }, { T_CHARPTR, offsetof(XX_Config, xx_product_name), "xx_product_name" }, { T_INT, offsetof(XX_Config, xx_max_size), "xx_max_size" }, { T_INT, offsetof(XX_Config, xx_min_size), "xx_min_size" }, { T_DOUBLE, offsetof(XX_Config, xx_ratio), "xx_ratio" }, }; enum { NUM_CONFIG = sizeof(xx_config) / sizeof(xx_config[0]) }; #ifndef lint /* Prevent over-aggressive optimizers from eliminating ID string */ extern const char jlss_id_offsetof_c[]; const char jlss_id_offsetof_c[] = "@(#)$Id$"; #endif /* lint */ int read_config(const char *file, XX_Config *config) { FILE *fp = fopen(file, "r"); if (fp == 0) return -1; char line[4096]; while (fgets(line, sizeof(line), fp) != 0) { if (is_comment_line(line)) continue; char *key; char *value; if (split(line, &key, &value) == 0) { Descriptor *desc = lookup(key); if (desc == 0) { err_report("Do not recognize key <<%s>>\n", key); continue; } TypeCode t = desc->type; if ((*info[t].valid)(value, desc, config) != 0) { err_report("Failed to convert <<%s>>\n", value); } } } fclose(fp); return 0; } int print_config(FILE *fp, const XX_Config *config) { for (int i = 0; i < NUM_CONFIG; i++) { char value[256]; TypeCode t = xx_config[i].type; if ((*info[t].format)(value, sizeof(value), &xx_config[i], (void *)config) == 0) fprintf(fp, "%-20s %s\n", xx_config[i].name, value); } return 0; } int val_integer(char *buffer, const Descriptor *descr, void *data) { int value; if (sscanf(buffer, "%d", &value) != 1) { err_report("Failed to convert <<%s>> to integer for %s\n", buffer, descr->name); return -1; } char *base = data; int *ptr = (int *)(base + descr->offset); *ptr = value; return 0; } int fmt_integer(char *buffer, size_t buflen, const Descriptor *descr, void *data) { char *base = data; int *ptr = (int *)(base + descr->offset); int nbytes; if ((nbytes = snprintf(buffer, buflen, "%d", *ptr)) < 0 || (size_t)nbytes >= buflen) { err_report("Failed to format %d into buffer of size %zu\n", *ptr, buflen); return -1; } return 0; } 

就像我在评论中所说:

出于实际目的,没有[ 没有一种简单的方法可以使用for循环来逐步遍历结构的元素来打印或初始化它们 ]。 你可以这样做(在查找offsetof() ),但是将其设置为正常工作是一项艰苦的工作 – 比接受你需要轮流打印每个成员要困难得多的工作要困难得多。

在原始场景的背景下,修改后的代码有一个好处 – 5000个删除的优势线!