我目前正在用OpenCL做c++,其中需要一个c风格的结构体来将配置信息从c++主机传送到OpenCL内核。鉴于动态分配的数组并不保证每个OpenCL实现都支持,我必须确保内核代码可访问的每个数组都是静态大小的。然而,在初始化c风格结构体中的静态数组时,我遇到了奇怪的错误。
以下PoC可能会重现该错误:
#include <cstring>
#include <string>
#define ID_SIZE 16
struct conf_t {
const unsigned int a;
const unsigned int b;
const unsigned char id[ID_SIZE];
};
int main() {
const std::string raw_id("0123456789ABCDEF");
unsigned char id[ID_SIZE];
memcpy(id,raw_id.c_str(),ID_SIZE);
struct conf_t conf = {10,2048,id};
}
并出现以下错误:
poc.cc: In function ‘int main()’:
poc.cc:15:39: error: array must be initialized with a brace-enclosed initializer
15 | struct conf_t conf = {10,2048,id};
| ^~
我确实可以删除结构体中的const关键字,并去掉堆栈变量id
,其中&(conf.id)
可以是memcpy
的第一个参数。但是,我希望保持conf结构体中字段的不可变性,这使编译器能够检查不需要的修改。
就我的理解而言,c中的struct应该具有以下内存布局:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| a |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ id +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
既然堆栈变量id
也是静态大小的,我很困惑为什么即使id
已经是一个静态大小的数组,c++编译器仍然要寻找一个用大括号括起来的初始化器。
4条答案
按热度按时间ujv3wf0j1#
如果你想复制整个字符串,你必须使用
memcopy
到conf.id
(或者strncpy
,如果它保证是一个以零结尾的字符串)。不幸的是,这意味着conf_t
中的id
不再是const
:另一方面,如果
conf_t.id
必须是const
,那么我相信你必须使用一个编译时常量初始化来保持conf_t
是一个POD类:也可以使用模板构造函数将动态数组转换为初始化列表。这将使您能够用动态数据初始化
const
c数组,但它会向conf_t
添加一个构造函数,这意味着它不再是POD类。这是可能的,我已经错过了一些虽然,所以我欢迎任何更正。
6qftjkof2#
请尝试以下语法之一:
0yg35tkg3#
你的声明有些奇怪。我怀疑你的大多数问题都来自const的滥用。
conf_t定义如下:
为什么要声明常量成员?为什么它们没有初始化?它们都声明为const,却没有一个被初始化,这让我怀疑这是一个很大的代码味道。如果你需要一个常量conf_t,那么就用通常的方式声明它,如下所示:
如果您需要更大的灵活性,(即:从左值初始化id),您可以按照@Frodyne的建议定义一个构造函数,或者创建一个初始化函数,这种解决方案的优点是保留了简单conf_t结构的POD属性。
示例:
这不会对手指和眼睛造成太大的负担。
iyfamqjs4#
初始化时的错误是无法使用
unsigned char[16]
类型的左值初始化const unsigned char
类型的数组元素。你需要初始化你的成员,使用正确的初始化语法和花括号
{ vals... }
,并使用正确的初始化值类型。但是,这在标准C++中并不容易,因为你试图从non-const
初始化const
值。一个不丢失
id
成员中的const
的解决方法是简单地将其更改为const unsigned char*
。然后,您可以使用
C++
中存在的一些模糊怪物,如reinterpret_cast<T>
,并从一个非常数成员初始化您的常量成员。现在一切都编译好了。您可以看到live example here