c - 使用指向指针数组成员的指针进行静态结构初始化

标签 c visual-studio visual-c++ c99

我正在尝试初始化一个需要指向指针数组的结构成员。这个想法是静态声明结构数组以避免初始化开销,因为所有数据都是固定的并且在编译时已知。

不幸的是,我无法在 Visual Studio 2015 下编译代码。下面列出的代码会产生以下错误:C2099: initializer is not a constant。这看起来很奇怪,因为 list 仅使用固定大小的字符串文字列表进行初始化。

#define DATA_LIST { \
    L"some",        \
    L"example",     \
    L"data",        \
    L"in a list",   \
    NULL            \
}

#define INFO_LIST { \
    L"another",     \
    L"list",        \
    L"with",        \
    L"some",        \
    L"info",        \
    NULL            \
}

typedef struct data {
    unsigned int flag;
    const wchar_t **list;
} dataset, *pdataset;

static dataset somedata[] = {
    {   .flag = 2,
        .list = (const wchar_t *[])DATA_LIST // C2099
    },
    {   .flag = 4,
        .list = (const wchar_t *[])INFO_LIST // C2099
    }
};

我还尝试过使用指向灵活数组的指针 (const wchar_t *list[];)。这不是理想的解决方案,因为 somedata 将无法再声明为结构数组。除此之外,它还会产生一个警告 (C4200: nonstandard extension used: zero-sized array in struct/union)

typedef struct data {
    unsigned int flag;
    const wchar_t *list[]; // C4200 (somedata can no longer be an array of structures)
} dataset, *pdataset;

static dataset somedata = {
    .flag = 2,
    .list = DATA_LIST
};

另一个想法是将 list 定义为指向固定大小的指针数组的指针。但这需要用一个足够大的list成员来定义dataset结构,以容纳最大的列表。当有很多小列表和一个大列表时也不理想。

typedef struct data {
    unsigned int flag;
    const wchar_t *list[sizeof (wchar_t *[])INFO_LIST / sizeof *(wchar_t *[])INFO_LIST];
} dataset, *pdataset;

static dataset somedata[] = {
    {   .flag = 2,
        .list = DATA_LIST
    },
    {   .flag = 4,
        .list = INFO_LIST
    }
};

也许我正在监督某些事情,或者是否有一些语言扩展功能可以提供一个优雅的解决方案?欢迎提出任何建议。

注意:即使添加了visual-c++标签,代码也会被编译为C代码。


另一个可能相关的有趣的事情是,当 somedata 被声明为非静态(因此没有 static 关键字)时,编译器将产生一些警告,但能够编译代码。通过将 somedata 声明为非静态,移除了强制用于初始化 somedata 的数据在编译时已知的约束。

如编译警告所示,似乎编译器在用它初始化 list 成员之前将字符串文字列表的地址临时存储在一个自动变量中。不过,这仍然是猜测。也许有经验的人可以阐明这里实际发生的事情。

typedef struct data {
    unsigned int flag;
    const wchar_t **list;
} dataset, *pdataset;

// C4221: nonstandard extension used: 'list': cannot be initialized using
//        address of automatic variable '$S1'
// C4204: nonstandard extension used: non-constant aggregate initializer
dataset somedata = {
    .flag = 2,
    .list = (const wchar_t *[])DATA_LIST // note: see declaration of '$S1'
};

最后但同样重要的是,当使用一个用字符串文字列表的地址初始化的临时变量来初始化 list 成员时,代码最终编译正常,没有任何警告或错误。

static const wchar_t *temp[] = DATA_LIST;

static dataset somedata = {
    .flag = 2,
    .list = temp
};

但是当将 temp 声明为指向指针的指针并对字符串文字列表进行类型转换时,代码无法再编译,因为初始化 list 的表达式被标记作为事件错误:expression must have a constant value

static const wchar_t **temp = (const wchar_t *[])DATA_LIST;

static dataset somedata = {
    .flag = 2,
    .list = temp // marked as active error
};

如果我随后决定再次使 somedata 成为非静态的,则该表达式不再被标记为事件错误。但是当尝试编译代码时,再次出现以下错误:C2099: initializer is not a constant

我想知道 Visual Studio 2017 是否以相同的方式运行,以及是否有替代方法可用于以类似方式组织和处理数据。

最佳答案

MSVC 不符合 C 标准。作为解决方法,您可以使用命名对象而不是复合文字:

static const wchar_t *x_data_list[] = DATA_LIST;
static const wchar_t *x_info_list[] = INFO_LIST;

static dataset somedata[] = {
    {   .flag = 2,
        .list = x_data_list
    },
    {   .flag = 4,
        .list = x_info_list
    }
};

我不确定您是否有意将列表设置为非 const,但如果您不打算在运行时写入 x_data_list,那么您可以将其设置为 const并为 .list 成员提供 const wchar_t * const * 类型。

关于c - 使用指向指针数组成员的指针进行静态结构初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47253563/

相关文章:

c - 原始数据类型的值超出范围和 C99 的 PRI* 宏的行为

c++ - 调试器不进入 MFC 源代码

c++ - CreateProcess 和 CreatePipe 执行进程并在 VC++ 中以字符串形式返回输出

visual-c++ - 如何在 C++ 中使用 NLog?

c - 在 Lua 中,我应该如何处理来自 C 的从零开始的数组索引?

c - 尝试在单链表中的某个位置之后插入节点,但它会在之前插入它

c - 为什么这个 getchar() 循环在输入一个字符后停止?

c++ - 为什么 Visual Studio 找不到我的 DLL?

visual-studio - 我应该使用什么开发工具在 F# 中编码?

c++ - Visual Studio C++ 2008/2010 - 浮点 NaN 中断