循环并将数据分配给结构成员的宏错误地将结构成员识别为指针

时间:2013-04-13 02:17:00

标签: c macros struct cycle type-conversion

我的问题是atoi正在将字符串的十六进制内存地址转换为十进制,而不是字符串中包含的内容。它是在宏中执行此操作。当宏定义使其成为int时,为什么将struct->成员解释为指针?下面的伪代码:

if (struct.member == int)? struct.member = atoi(data) : struct.member = data;

该程序的这一部分的目的是从包含有关结构属性的信息的.csv文件中检索数据。我可以接受一个“id”并将每个单元格字符串存储到一个字符串数组中(csvRowSplit)。

然而,我然后想要将数组的内容传输到包含不同数据类型的结构(我想用来检索玩家保存的属性,攻击方法,商店项目等的方法)。硬编码很容易:

opponent->att = atoi(csvSplitRow[0]);
opponent->= atoi(csvSplitRow[1]);
opponent->hitpoints = atoi(csvSplitRow[2]);
opponent->description = csvSplitRow[3]);

然而,这会使结构成员更加混乱,并且不够灵活或不可重复。

我已经定义了一个宏来遍历结构的元素,并将csvSplitRow []与变量配对,如果需要转换为atoi。

#define X_FIELDS \
    X(char*, description, "%s") \
    X(int, xpreward, "%d") \
    X(int, att, "%d") \
    /* ... */
    X(int, crit, "%d")

typedef struct
{
    #define X(type, name, format) type name;
        X_FIELDS
    #undef
}

void update_opp(char** csvSplitRow, opp* opponent)
{
    int i = 0;
    #define X(type, name, format) \
        if (strcmp(format, "%d") == 0) \          // if an int, convert it
            opponent->name = atoi(csvSplitRow[i]); \
        else \                                    // otherwise put it straight in
            opponent->name = csvSplitRow[i]; \
        i++;
    X_FIELDS
    #undef X
}

直接赋予字符串成员的工作(即没有转换),但是atoi导致将十六进制内存地址转换为整数,而不是它指向的字符串。

// before conversion
csvRowSplit[1] == 0x501150 "20"

// practice
atoi(csvRowSplit[1]) == 20

// after conversion and storing in struct int member
opponent->xpreward = atoi(csvSplitRow[1]);
opponent->xpreward == 5247312           // the decimal equivalent of 0x501150

我不知道我现在能做什么,除了硬编码,每次我想要将一个解析过的csv与一个结构相匹配。请帮忙!

编辑: 我用-Werror得到编译时错误:

error: assignment makes integer from pointer without a cast [-Werror]

错误在update_opp函数的宏内。但是,我知道它不是指针,因为我之前将它定义为int?那为什么不承认呢?我不能施展它,所以我该怎么办?

1 个答案:

答案 0 :(得分:1)

您的问题出在以下代码中:

#define X(type, name, format) \
    if (strcmp(format, "%d") == 0) \          // if an int, convert it
        opponent->name = atoi(csvSplitRow[i]); \
    else \                                    // otherwise put it straight in
        opponent->name = csvSplitRow[i]; \
    i++;

对于任何给定的属性名称(att说),您得到:

    if (strcmp("%d", "%d") == 0)
        opponent->att = atoi(csvSplitRow[i]);
    else
        opponent->att = csvSplitRow[i];
    i++;

除了它全部在一条线上。但是,关键的一点是,您要么将int(从atoi())分配给字符串,要么在每次调用时将字符串分配给int,其中一个是错误的。在优化之前,代码必须是正确的。

如何解决?这很棘手。我想我可能会使用这样的东西:

#include <stdlib.h>

#define CVT_INT(str)    atoi(str)
#define CVT_STR(str)    str

#define X_FIELDS \
    X(char*, description, "%s", CVT_STR) \
    X(int, xpreward, "%d", CVT_INT) \
    X(int, att, "%d", CVT_INT) \
    /* ... */ \
    X(int, crit, "%d", CVT_INT)

typedef struct opp
{
#define X(type, name, format, converter) type name;
    X_FIELDS
#undef X
} opp;

extern void update_opp(char** csvSplitRow, opp* opponent);

void update_opp(char** csvSplitRow, opp* opponent)
{
    int i = 0;

#define X(type, name, format, converter) \
    opponent->name = converter(csvSplitRow[i++]);

    X_FIELDS

#undef X
}

在非常严格的编译器标志下编译时没有警告:

gcc -pedantic -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition -c xm.c

当需要执行其他操作时,可以重新定义CVT_INTCVT_STR宏。


该代码的替代版本更广泛地利用了CVT_INTCVT_STR(重命名为X_INTX_STR):

#include <stdlib.h>

#define X_FIELDS \
    X(X_STR, description) \
    X(X_INT, xpreward) \
    X(X_INT, att) \
    /* ... */ \
    X(X_INT, crit)

typedef struct opp
{
#define X_INT char *
#define X_STR int
#define X(code, name) code name;
    X_FIELDS
#undef X
#undef X_INT
#undef X_STR
} opp;

extern void update_opp(char** csvSplitRow, opp* opponent);

void update_opp(char** csvSplitRow, opp* opponent)
{
    int i = 0;

#define X_INT(str)    atoi(str)
#define X_STR(str)    str
#define X(converter, name) \
    opponent->name =  converter(csvSplitRow[i++]);

    X_FIELDS

#undef X
#undef X_INT
#undef X_STR
}

由于多个#define#undef操作,我不是100%确信这更好,但在某些方面它更接近最小(它不需要{{1}例如,vs "%d"字段 - 至少不在显示的代码中。)