C++:如何将静态数组分配给结构体内的指针

3

我有一段C代码,需要转换成C++,并尽量最小化修改。

在C语言中,以下代码是有效的:

typedef struct _type_t
{
    int a;
    int b;
    int c[];
}type_t;

type_t var = {1,2,{1,2,3}};

但在C++11中,它会产生错误:

错误:对于int [0]过多的初始值

但我不能给type_t.c一个固定的大小,因为它需要适用于任何大小的数组。

所以我需要改变这个结构体:

typedef struct _type_t
    {
        int a;
        int b;
        int *c;
    }type_t;

但是我需要改变

的内容


type_t var = {1,2,{1,2,3}};

由于当前代码存在错误,需要将其更改为其他内容

错误:对于类型int*,标量初始化程序周围的大括号

将第三个元素强制转换为(int[])会导致错误

错误:取临时数组的地址

这段代码来自micropython, parse.c:

#define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { __VA_ARGS__ } };

我该如何初始化数组并将其赋值给type_t.c

1
gcc在编译您的C代码时会发出警告:“warning: initialization of a flexible array member [-Wpedantic]”(gcc -Wall -pedantic -std=c11)。 - user2672107
2
你需要知道c的大小来完成这个任务。例如,如果int c[];变成了int c[3];那么它就能够正常工作了。 - DimChtz
2
@Adrian 为什么会呢?C 不是 C++,C++也不是 C。 - DimChtz
3
@Adrian - 不,它在C语言中不起作用。它在你以前的编译器下工作,该编译器提供了一种扩展。C语言规范明确禁止此操作。 - StoryTeller - Unslander Monica
1
@StoryTeller 我想你是对的:这不符合C标准,但在gcc中可以工作。 - Bob
显示剩余3条评论
4个回答

7

使用 std::vector聚合初始化

struct type_t
{
    int a;
    int b;
    std::vector<int> c;
};

int main() {
    type_t var = { 1, 2, { 1, 2, 3 } };
}

那不是一个选项。我需要尽可能少地修改它,即不引入使用STL。 - Bob
4
当然可以。这是首选的选项。 - Ron
然后我必须修改所有使用 type_t 的代码。 - Bob
@Adrian std::vector已经重载了operator[],因此您仍然可以像访问数组元素一样访问存储在std::vector中的元素。 - Algirdas Preidžius
@Zinki 我很想帮忙,但我需要将每个文件编译为C++。 - Bob
4
@Adrian,那么你需要认识到C++不同于C,有些事情你需要以与C不同的方式来处理。 - Zinki

6
为了实现这一点,您需要使数组真正静态:
typedef struct _type_t
{
    int   a;
    int   b;
    int * c;
}type_t;

int items[3] = {1,2,3};
type_t var = {1,2, static_cast< int * >(items)};

既然这个方法可行,为什么不能在“{}”内部声明一个数组呢? - Bob
3
@Adrian - 因为 C++ 不允许。你不能强制该语言成为它不是的样子。 - StoryTeller - Unslander Monica
这个解决方案比得票最高的方案更符合将现有的C代码转换为C++的任务。 - Jeffrey
@Jeffrey - 没有冒犯之意,但您认为这更符合要求吗? - StoryTeller - Unslander Monica
1
@StoryTeller 这将使对象的内存占用与之前相同。可能某处存在对_type_t.c强制类型转换为int*的访问,或者是假定特定大小的_type_t的memcpy,或者添加std::vector的复杂度。我曾经转换过遗留代码库,通常来说,改动越少越好,即使最终得到的是像这样的hackish东西。 - Jeffrey
2
@Jeffrey - 老实说,我认为正确的转换方式是将其作为带有扩展功能的C语言进行编译。只需将其紧密地包裹在符合标准的代码防护服中,然后让它保持不变即可。 - StoryTeller - Unslander Monica

1

C++不允许使用这个语句表达式。您可以使用轻量级的std::initialiser_list来实现目标。

#include <initializer_list>
typedef struct _type_t
{
    int a;
    int b;
    std::initializer_list<int> c;
}type_t;

 type_t var = {1,2,{1,2,3,4}};

initializer_list 不提供 operator[]。原 PO 不能将其用作一种投入替换。 - StoryTeller - Unslander Monica
讲故事的人:是的,它不提供下标,必须使用迭代器来遍历。但与向量相比,它非常轻巧,根据需求可以选择使用它。 - DeepakKg
1
我担心它太轻量级了。首先,需要考虑对象生命周期的问题。成员变量 c 是否会延长从 {1, 2, 3, 4} 创建的临时数组的生命周期?我认为不会。由于 OP 还想要一个可替换的解决方案,这并没有帮助到这里。 - StoryTeller - Unslander Monica
它也不够轻量级,无法保证这样的type_t仍然是POD类型;因此即使它是正确的,与std::vector相比你也几乎没有任何收益。 - Massimiliano Janes
我猜函数/程序的要求应该决定选择什么。 它是一个选项,可能不适用于每种情况,但在内存和复杂性方面,它绝对比向量更好,因为没有动态增长的要求(根据上面的代码)。 - DeepakKg

0

另一个选择可能是

    ...
    int * c;
}type_t;

type_t var = {1,2, []{ static int x[] = {1,2,3}; return x; }() };

顺便说一句:我知道这很疯狂,但它更尊重 OP 的要求(可以作为一个插入替换宏来使用)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接