使用指向成员

时间:2017-11-12 20:24:58

标签: 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定义为指向固定大小的指针数组的指针。但这需要使用dataset成员定义list结构,该成员足够大以容纳最大列表。当有许多小清单和一个单一的大清单时,也不理想。

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的表达式被标记为活动错误:{ {1}}

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行为方式是否相同,是否有可用的替代方法以类似的方式组织和处理数据。

1 个答案:

答案 0 :(得分:2)

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并提供{ {1}}成员类型.list