3

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

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 ?那它为什么不承认呢?我不能施放它,那我该怎么办?

4

1 回答 1

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(from atoi()) 分配给字符串,要么int在每次调用中将字符串分配给 an,而这两者中的一个是错误的。代码在优化之前必须是正确的。

怎么修?这很棘手。我想我可能会使用这样的东西:

#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_INTand宏。CVT_STR


代码的替代版本更广泛地利用CVT_INTCVT_STR(重命名为X_INT和):X_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
}

我不是 100% 相信这会更好,因为有多个#define#undef操作,但它在某些方面几乎是最小的(例如,它不需要"%d"vs"%s"字段 - 至少在显示的代码中不需要)。

于 2013-04-13T03:49:56.110 回答