有没有一种方法来打印在一个循环结构成员不点名用C每个成员? [英] Is there a way to print struct members in a loop without naming each member in C?

查看:97
本文介绍了有没有一种方法来打印在一个循环结构成员不点名用C每个成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每次我想打印或初始化一个struct我必须去通过每个成员作出code不太重用。有没有办法做到这一点在for,while或者while循环?做

  typedef结构客户端
{
   字符*名称;
   字符*地址;
   字符*密码;
   字符*特权;
}客户;


解决方案

下面是一个使用例子 offsetof()。在code是不完整的,但什么是present并编译,假设你使用C99或C11编译器。在code是不是在最小的地方 - 特别是 val_integer() fmt_integer()功能。你可以避开 PTR 中的每个变量通过编写复杂的前pression。

这是基于是该方案从文本文件到一个结构读取配置。该文件中的行可以为空或包含注释(到行尾),或形式的配置参数:

  NAME_OF_PARAMETER价值的参数

该名后跟空格和值。不同的配置元件是不同的类型。在code读取的行,分裂非注释行到一个键(参数名)和值,然后调用code的值转换为正确类型的关键。在配置文件此方案是基于,大约有三百配置参数,以及不同的参数可具有不同的有效范围,和默认值,等等,因此,该组的类型是相当大的,因此,但这是不够好为了说明的目的。 真正的计划是比这更复杂,因为大约有30双验证和格式化功能 - 但它拥有超过6000线程式化一套300单元的20行,每行取代了单一功能与一个大大较小的源文件不到一千行总共也没有超过功能约20行(全尺寸数字近似值)。

 的#include<&STDDEF.H GT;
#包括LT&;&stdio.h中GT;/ *#包括xxconfig.h//将定义结构XX_Config * /
typedef结构XX_Config
{
    INT xx_version_major;
    INT xx_version_minor;
    字符* xx_product_name;
    INT xx_max_size;
    INT xx_min_size;
    双xx_ratio;
    / *等了几百年的配置元素* /
} XX_Config;类型定义枚举类型code
{
    着色,
    T_DOUBLE,
    T_CHARPTR,
    T_CHARARR,
    / * ...其它类型的需要* /
}输入code;
typedef结构描述符描述;
typedef结构所属类别所属类别;的typedef INT(*验证)(字符*缓冲,常量描述符* DESCR,无效*数据);
的typedef INT(*格式化)(字符*缓冲区,为size_t buflen,常量描述符* DESCR,无效*数据);所属类别结构
{
    输入code型;
    验证有效;
    格式化格式;
};结构描述
{
    输入code型;
    SIZE_T抵消;
    字符*名称;
};EXTERN INT val_integer(字符*缓冲,常量描述符* DESCR,无效*数据);
EXTERN INT val_double(字符*缓冲,常量描述符* DESCR,无效*数据);
EXTERN INT val_charptr(字符*缓冲,常量描述符* DESCR,无效*数据);EXTERN INT fmt_integer(字符*缓冲区,为size_t buflen,常量描述符* DESCR,无效*数据);
EXTERN INT fmt_double(字符*缓冲区,为size_t buflen,常量描述符* DESCR,无效*数据);
EXTERN INT fmt_charptr(字符*缓冲区,为size_t buflen,常量描述符* DESCR,无效*数据);EXTERN无效err_report(为const char * FMT,...);
EXTERN INT read_config(为const char *文件,XX_Config *配置);
EXTERN INT print_config(FILE * FP,常量XX_Config *配置);/ *将是静态的 - 但他们没有定义,因此,他们需要的extern * /
EXTERN INT is_comment_line(字符*线);
EXTERN描述符*查找(字符*键);
EXTERN INT分(字符*线,焦**键,字符**值);静态所属类别信息[] =
{
    [T_CHARPTR] = {T_CHARPTR,val_charptr,fmt_charptr},
    [T_DOUBLE] = {T_DOUBLE,val_double,fmt_double},
    [T_INT] = {T_INT,val_integer,fmt_integer},
    // ...其他类型的需要
};静态描述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},
};枚举{NUM_CONFIG = sizeof的(xx_config)/的sizeof(xx_config [0])};皮棉的#ifndef
/ * prevent过分进取,从消除ID字符串优化* /
EXTERN为const char jlss_id_offsetof_c [];
为const char jlss_id_offsetof_c [] =@(#)的$ id $;
#ENDIF / *皮棉* /INT read_config(为const char *文件,XX_Config *配置)
{
    FILE * FP = FOPEN(文件,R);
    如果(FP == 0)
        返回-1;
    焦线[4096];    而(与fgets(线,的sizeof(线),FP)!= 0)
    {
        如果(is_comment_line(线))
            继续;
        字符*键;
        字符*值;
        如果(分(行和放大器;键,&安培;价值)== 0)
        {
            描述* DESC =查找(键);
            如果(DESC == 0)
            {
                err_report(不承认键和LT;<%S>> \\ N键);
                继续;
            }
            类型code T = desc->类型;
            如果((*资讯[T] .valid)(价值递减,配置)!= 0)
            {
                err_report(未能转换<<%S>> \\ N,值);
            }
        }
    }    FCLOSE(FP);
    返回0;
}INT print_config(FILE * FP,常量XX_Config *配置)
{
    的for(int i = 0; I< NUM_CONFIG;我++)
    {
        char值[256];
        类型code T = xx_config [I] .TYPE;
        如果((*资讯[T] .format)(价值的sizeof(值),放大器; xx_config [I],(无效*)配置)== 0)
            fprintf中(FP,%-20s%S \\ n,xx_config [I] .name和值);
    }
    返回0;
}INT val_integer(字符*缓冲,常量描述符* DESCR,无效*数据)
{
    int值;
    如果(sscanf的(缓冲器,%d个,&放大器;!值)= 1)
    {
        err_report(未能转换<<%S>>到整数%s的\\ n,缓冲,descr->名);
        返回-1;
    }
    字符*基地=数据;
    为int * PTR =(INT *)(基地+ descr->抵消);
    * PTR =价值;
    返回0;
}INT fmt_integer(字符*缓冲区,为size_t buflen,常量描述符* DESCR,无效*数据)
{
    字符*基地=数据;
    为int * PTR =(INT *)(基地+ descr->抵消);
    INT为nbytes;
    如果((为nbytes = snprintf的(缓冲器,buflen,%d个,* PTR))℃,||(为size_t)为nbytes&GT = buflen)
    {
        err_report(无法​​%D格式为大小%祖\\ n个缓冲区,* PTR,buflen);
        返回-1;
    }
    返回0;
}

就像我在评论中说:


  

有关实际目的,没有[没有使用循环来通过一个结构的元素打印或初始化它们的简单方法的。你可以这样做(查找 offsetof()< STDDEF.H> ),但最多将其设置为正常工作是艰苦的工作 - 不是接受,你需要打印依次在每个成员,通常是最小的利益更难的工作。


在原始方案的背景下,出现了对订正code进行利益! - 5000删除的益处线

Each time I want to print or initialize a struct I have to go through each member making the code not very reusable. Is there a way to do this in a for, while or do while loop?

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

解决方案

Here is an example using offsetof(). The code isn't complete, but what's present does compile, assuming you use a C99 or C11 compiler. The code isn't minimal in places — notably the val_integer() and fmt_integer() functions. You could avoid the base and ptr variables in each by writing a complex expression.

The scenario it is based on is 'read a configuration from a text file into a structure'. The lines in the file can be blank or contain a comment (# to end of line), or a configuration parameter in the form:

NAME_OF_PARAMETER   value-for-parameter

The name is followed by white space and the value. Different configuration elements are of different types. The code reads the line, splits non-comment lines into a key (parameter name) and value, and then calls code to convert the value to the correct type for the key. In the configuration file this scheme is based on, there were about three hundred configuration parameters, and different parameters could have different valid ranges, and defaults, and so on, so the set of types was considerably larger, therefore, but this is good enough for illustrative purposes. The 'real' scheme was considerably more complex than this as there were about 30 pairs of validation and formatting functions — but it replaced a single function with over 6,000 lines in stylized set of 300 units of 20 lines each with a vastly smaller source file of less than a thousand lines in total and no function larger than about 20 lines (all size figures approximations).

#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;
}

Like I said in a comment:

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.

In the context of the original scenario, there was a benefit to the revised code — 5000 deleted lines of benefit!

这篇关于有没有一种方法来打印在一个循环结构成员不点名用C每个成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆