c - 有没有办法在循环中打印结构成员而无需在 C 中命名每个成员?

标签 c loops struct

每次我想打印或初始化一个结构时,我都必须遍历每个成员,这使得代码的可重用性不高。有没有办法在 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

名称后跟空格和值。不同的配置元素具有不同的类型。代码读取该行,将非注释行拆分为键(参数名称)和值,然后调用代码将值转换为键的正确类型。在这个方案所基于的配置文件中,大约有三百个配置参数,不同的参数可以有不同的有效范围,默认值等等,所以类型集相当大,因此,但这已经足够了出于说明目的。 “真正的”方案比这复杂得多,因为大约有 30 对验证和格式化函数——但它用 6,000 多行代码替换了一个函数,在 300 个单元的程式化集合中,每个单元 20 行,每个单元都有一个小得多的源文件总共少于 1000 行,并且没有函数超过 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 practical purposes, no [there isn't an easy way to use a for loop to step through the elements of a structure to print or initialize them]. You can do it (look up offsetof() in <stddef.h>), but setting it up to work properly is hard work — much harder work than accepting that you'll need print each member in turn, and usually for minimal benefit.

在原始场景的上下文中,修改后的代码有一个好处——删除了 5000 行的好处!

关于c - 有没有办法在循环中打印结构成员而无需在 C 中命名每个成员?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27496245/

相关文章:

c - 如何使用 Rembedded 解析因子/数据帧

javascript - 根据对象数组中的条目向循环生成的元素添加 css 类

r - 与 data.frame 中的交替行组进行比较时的计数频率

c++ - 如何在结构体中声明填充?

c++ - 从堆中为数组分配地址

与数据结构对齐混淆?

c 分段核心转储?

c - 如何将NULL存储到节点中?

c - 二维字母数组的唯一键

java - 优化具有多个循环的方法