每次我想要打印或初始化结构时,我都必须通过每个成员使代码不能重复使用。有没有办法在for,while或do while循环中执行此操作?
typedef struct Client
{
char* Name;
char* Address;
char* Password;
char* Privilege;
}Client;
答案 0 :(得分:3)
以下是使用offsetof()
的示例。代码是不完整的,但假设您使用C99或C11编译器,那么现在编译的是什么。代码在某些地方并不是最小的 - 特别是val_integer()
和fmt_integer()
函数。您可以通过编写复杂表达式来避免每个变量中的base
和ptr
变量。
它所基于的场景是“从文本文件中读取配置到结构中”。文件中的行可以为空或包含注释(#
到行尾)或表单中的配置参数:
NAME_OF_PARAMETER value-for-parameter
名称后跟空格和值。不同的配置元素具有不同的类型。代码读取行,将非注释行拆分为键(参数名称)和值,然后调用代码将值转换为键的正确类型。在这个方案的配置文件的基础上,有大约300个配置参数,不同的参数可以有不同的有效范围,默认值等等,所以类型集相当大,因此,这是足够好的用于说明目的。真实的'方案比这要复杂得多,因为它有大约30对验证和格式化功能 - 但它取代了一个单独的功能,超过6,000行,每个包含300个单位,每行20个,每个都有一个小于千的源文件。总共行,没有大于约20行的函数(所有大小的数字近似值)。
#include <stddef.h>
#include <stdio.h>
/* #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()
中查找<stddef.h>
),但将其设置为正常工作是一项艰苦的工作 - 比接受你需要轮流打印每个成员要困难得多,并且通常是为了最低限度的利益。
在原始场景的背景下,修改后的代码有一个好处 - 5000个删除的优势线!