【问题标题】:Is there a way to print struct members in a loop without naming each member in C?有没有办法在循环中打印结构成员而不用 C 命名每个成员?
【发布时间】:2015-02-14 06:27:37
【问题描述】:

每次我想打印或初始化一个结构时,我都必须遍历每个成员,这使得代码不能很好地重用。有没有办法在 for、while 或 do while 循环中做到这一点?

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

【问题讨论】:

  • 你能举个例子吗?
  • 出于实用目的,没有。你可以做到(在<stddef.h> 中查找offsetof()),但设置它以使其正常工作是一项艰苦的工作——比接受你需要依次打印每个成员要困难得多,而且通常是为了最小的利益。
  • 曾经可以使用宏魔术和内联文件和疯狂的定义。我曾经那样做过类似的事情。
  • 如果你只需要值(不是字段名),你可以这样做:stackoverflow.com/questions/5524552/…
  • 我来研究一下X-Macro,好像是60年代的。哇!为什么这么不为人知?

标签: c loops struct


【解决方案1】:

这是一个使用offsetof() 的示例。代码不完整,但假设您使用 C99 或 C11 编译器,现在的代码确实可以编译。代码不是最少的地方——特别是val_integer()fmt_integer() 函数。您可以通过编写一个复杂的表达式来避免每个变量中的baseptr

它基于的场景是“将配置从文本文件读取到结构中”。文件中的行可以是空白的,也可以包含注释(# 到行尾),或以下形式的配置参数:

NAME_OF_PARAMETER   value-for-parameter

名称后跟空格和值。不同的配置元素具有不同的类型。代码读取该行,将非注释行拆分为键(参数名称)和值,然后调用代码将值转换为键的正确类型。在这个方案所基于的配置文件中,大约有三百个配置参数,不同的参数可以有不同的有效范围、默认值等等,所以类型的集合是相当大的,因此,但这已经足够了用于说明目的。 “真正的”方案比这复杂得多,因为大约有 30 对验证和格式化函数——但它用 300 个单元(每个单元 20 行)的风格化集合中超过 6,000 行的单个函数替换了一个非常小的源文件总共不到 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 循环来逐步遍历结构的元素以打印或初始化它们]。你可以做到(在&lt;stddef.h&gt; 中查找offsetof()),但是设置它以使其正常工作是一项艰苦的工作——比接受你需要依次打印每个成员要困难得多,而且通常是为了最小的利益。

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

【讨论】:

  • 考虑到避免在 printf 中命名成员所涉及的努力,您已经证明了两件事 (1) 您还没有孩子(这会利用时间) ,并且 (2) 远远超出了职责范围 - 再次。干得好。 :)
猜你喜欢
  • 2021-11-22
  • 2013-10-12
  • 1970-01-01
  • 1970-01-01
  • 2022-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-23
相关资源
最近更新 更多