我在C ++中有一个结构如下:
typedef struct DIFDict
{
std::string name;
std::string defs[];
struct DIFColor *colors[];
uint8_t defsize;
} DIFDict;
问题在于尝试初始化它。我有一个函数,它返回一个指向结构的指针,其中所有值都已初始化:
struct DIFDict *CreateDIFDict(std::string n, std::string *d, struct DIFColor **c, uint8_t de)
{
int memsize = sizeof(n) + sizeof(d) + sizeof(c) + sizeof(de);
struct DIFDict *i = static_cast<struct DIFDict*>(malloc(memsize));
if(i != NULL)
{
i->name = n;
i->defs = d;
i->colors = c;
i->defsize = de;
}
return i;
}
然而,编译器抱怨类型不匹配:
error: incompatible types in assignment of 'std::string {aka std::basic_string<char>}' to 'std::string [0] {aka std::basic_string<char> [0]}'
error: incompatible types in assignment of 'DIFColor**' to 'DIFColor* [0]'
我是否误解了指针和数组之间的关系?
答案 0 :(得分:2)
typedef struct DIFDict
{
std::string name;
std::string *defs;
struct DIFColor **colors;
uint8_t defsize;
} DIFDict;
struct DIFDict *CreateDIFDict(std::string n, std::string *d, struct DIFColor *c, uint8_t de)
{
DIFDict *i = 0;
i = new DIFDict;
if(i != 0)
{
i->name = n;
i->defs = d;
i->colors = &c;
i->defsize = de;
return i;
}
return 0;
}
编辑:上面答案的问题是DIFColor
参数是临时的,当函数退出时,指向临时的指针将无效。一个更加可行的(不是最好的,但至少是可行的)解决方案与上述内容密切相关,如下所示:
struct DIFDict
{
std::string name;
std::string *defs;
DIFColor **colors;
uint8_t defsize;
};
DIFDict *CreateDIFDict(const std::string& n, std::string *d,
DIFColor **c, uint8_t de)
{
DIFDict *i = new DIFDict;
i->name = n;
i->defs = d;
i->colors = c;
i->defsize = de;
return i;
}
请注意,我们现在不接受临时的地址 - 我们直接传递指针值并分配它。另请注意,new
在失败时不返回0(除非使用nothrow
选项),因此new
为0时不需要检查。
然而,这个解决方案仍然是类似C的并且容易出错,并且有更好的方法,如其他答案所述。
答案 1 :(得分:2)
你似乎对C ++中的数组有一些基本的误解。它们具有固定的大小,在编译时已知。零大小的数组是非法的。 (有些编译器在标准模式下不调用时允许使用零大小的数组,但是它们的语义不清楚,当然也不能存储任何值。)
您也错误地使用sizeof
。 sizeof x
告诉您存储x
需要多少字节。 (x
可能指向的任何内容,如果x
是指针,而不是x
是句柄或容器类的任何内容。例如,std::string
是一个容器,其指针指向动态分配的字符串内容。 sizeof name
只能获取容器大小,它不会为您提供动态分配的空间。
在您的代码中,memsize
可以在编译时确定;对函数的不同调用永远不会有所不同。
此外,使用函数CreateDIFDict
的风格很差,因为它强制动态分配。相反,提供一个构造函数。然后用户可以选择是否需要动态分配,并且可以利用移动和复制语义。
目前尚不清楚您是否希望DIFDict
获取正在初始化它的字符串和DIFColors的副本。如果您不想要副本:
struct DIFDict
{
std::string name;
std::string *defs;
struct DIFColor **colors;
uint8_t defsize;
DIFDict(std::string name, std::string *defs, struct DIFColor **colors, uint8_t defsize) :
name(name), defs(defs), colors(colors), defsize(defsize) {}
};
确保在销毁指针所指向的内容之前销毁所有DIFDict
s。 (原始指针在C ++中没有所有权语义)。
如果您确实需要副本:
struct DIFDict
{
std::string name;
std::vector<std::string> defs; // or some other container
// ??? colors
DIFDict(std::string name, std::string const *p_defs, uint8_t defsize)
: name(name), defs(p_defs, p_defs + defsize) {}
};
我不确定colors
应该指向什么,如果您提供有关颜色的预期行为的更多信息,我将更新此示例。
答案 2 :(得分:1)
这是C ++,而不是C.删除typedef并像对待类一样初始化它。 struct
和class
之间的唯一区别是成员的默认可见性。类似的东西:
struct DIFDict
{
string name;
vector<string> defs;
DIFColor* colors;
uint8_t defsize;
DIFDict();
~DIFDict();
};
DIFDict::DIFDict : name(""), defs(...), colors(...), defsize(0) {}
DIFDict::~DIFDict() {...}
正如0x499602D2指出的那样,您应该为数组使用vector
。
省略号意味着创建一个帮助程序来初始化成员。帮助器应确保构造函数在异常期间按预期运行。