使用标量初始化器初始化字符串数组

时间:2021-02-06 08:41:38

标签: arrays c string initialization

我是 C 的新手,如果这个问题变得微不足道,那么抱歉。我已经使用宏(目前)编写了一个项目结构初始化,它接受一个方法和一个字符串数组,用作方法参数。最后一个参数的初始化会引发有关标量初始化程序中元素过多的警告。

该项目是一个包含名称、方法和方法参数的小结构。如您所见,最后一个字段被声明为一个字符串数组。

// Definition of mcl_item; aka. mitem
struct mcl_item {
    char *name;
    void (*method)(char *args[]);
    char **args;
};
#define mitem struct mcl_item

// Create a new mitem (user will use item and item0 macros instead)
mitem new_mitem(char name[MAX_STRLEN], void (*method)(char **), int n, char *arg, ...) {
    // return simple mitem without arguments allocation
    return((mitem){
        .name = name,
        .method = method,
        .args = 0
    });
}

这就是我的宏的样子,这里也让我觉得标量初始化应该可以工作:

// Define arguments amount by a macro
#define ARGSN(...) (int)(sizeof((char *){__VA_ARGS__})/sizeof(char *))
// Overload new_mitem() to avoid taking n parameter storing generated size
#define NEW_MITEM(name, method, ...) \
    new_mitem(name, method, ARGSN(__VA_ARGS__), (char *){__VA_ARGS__})
// Top level macro for creating item with arguments
#define item(name, method, ...) NEW_MITEM(name, method, __VA_ARGS__)
// Top level macro for creating item without arguments
#define item0(name, method) NEW_MITEM(name, method, 0)

然后在主程序中初始化项目并访问它们。

// Boilerplate methods used by items
void hello(char *args[]);
void extend(char *args[]);

int main() {
    // initialize items
    mitem item1 = item("extend", &extend, "Charlie", "Delta");
    mitem item2 = item0("hello", &hello);
    // access item1
    printf("Accessing item: %s\n", item1.name);
    item1.method(item1.args);
    // access item2
    printf("Accessing item: %s\n", item2.name);
    item2.method(item2.args);
    
    return(0);
}

// Boilerplate methods definitions
void hello(char *args[]) {
    char name[MAX_STRLEN] = "Bob";
    if (!args[0]) {
        strcpy(name, args[0]);
    }
    printf("Hello %s!\n", name);
}

void extend(char *args[]) {
    hello(args);
    printf("Welcome %s.\n", args[1]);
}

带有 -E 标志预编译的 gcc 响应:

<source>:43:91: warning: excess elements in scalar initializer
   43 |     struct mcl_item item1 = new_mitem("extend", &extend, (int)(sizeof((char *){"Charlie", "Delta"})/sizeof(char *)), (char *){"Charlie", "Delta"});
      |                                                                                           ^~~~~~~
<source>:43:91: note: (near initialization for '(anonymous)')
<source>:43:138: warning: excess elements in scalar initializer
   43 |     struct mcl_item item1 = new_mitem("extend", &extend, (int)(sizeof((char *){"Charlie", "Delta"})/sizeof(char *)), (char *){"Charlie", "Delta"});
      |                                                                                                                                          ^~~~~~~
<source>:43:138: note: (near initialization for '(anonymous)')

1 个答案:

答案 0 :(得分:1)

在使用时,(char *){__VA_ARGS__} 被替换为 (char *){"Charlie", "Delta"}。这表示创建一个 char * 并使用 "Charlie""Delta" 对其进行初始化。但是,char * 是一回事(指向 char 的指针),而 "Charlie""Delta" 是两件事。因此编译器会警告您初始化程序过多。

您可以使用 (char *[]){__VA_ARGS__} 创建指向 char * 的指针数组。当用作 sizeof 的操作数(或用作一元 & 的操作数)时,这将给出数组的大小,因此它将在 ARGSN 宏中工作。否则,数组将自动转换为指向其第一个元素的指针,因此可用于初始化 args 成员或其他 char ** 对象。

但是,您的 new_mitem 函数不接受 char ** 参数,而是声明 char *arg, ...。您将要传递它 __VA_ARGS__ 或将其更改为 char **arg(没有 ...)并传递它 (char *[]){__VA_ARGS__}

相关问题