我目前正在使用 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 中的結構應具有以下記憶體布局:
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 編譯器仍會尋找大括號括起來的初始值設定項。
uj5u.com熱心網友回復:
正如@Quimby 的評論所說,目前接受的答案是錯誤的:它只將第一個字符從復制id到conf.id.
如果你想復制整個字串,你必須使用memcopyinto conf.id(或者strncpy如果它保證是一個零終止的字串)。不幸的是,這意味著idinconf_t不能再存在const了:
#include <iostream>
#include <cstring>
#include <string>
#define ID_SIZE 16
struct conf_t
{
const unsigned int a;
const unsigned int b;
unsigned char id[ID_SIZE];
};
int main()
{
const std::string raw_id("0123456789ABCDE");
conf_t conf = {10, 2048, {0}};
memcpy(conf.id, raw_id.c_str(), ID_SIZE); // <- memcopy from the string into conf.id
std::cout << conf.id << '\n';
std::cout << std::boolalpha;
std::cout << std::is_pod<conf_t>::value << '\n';
}
另一方面,如果conf_t.id必須是const,那么我相信您必須使用編譯時常量初始化來保留conf_tPOD 類:
struct conf_t
{
const unsigned int a;
const unsigned int b;
const unsigned char id[ID_SIZE];
};
int main()
{
conf_t conf = {10, 2048, "0123456789ABCDE"};
...
也可以使用模板建構式將動態陣列轉換為初始化串列。這將使您能夠const使用動態資料初始化 c 陣列,但它添加了一個建構式,conf_t這意味著它不再是 POD 類。
#include <iostream>
#include <cstring>
#include <string>
#include <utility>
#define ID_SIZE 16
struct conf_t
{
const unsigned int a;
const unsigned int b;
const unsigned char id[ID_SIZE];
conf_t(const unsigned int a,
const unsigned int b,
const unsigned char (&arr)[ID_SIZE])
: conf_t(a, b, arr, std::make_index_sequence<ID_SIZE>())
{
}
private:
template <std::size_t... Is>
conf_t(const unsigned int a,
const unsigned int b,
const unsigned char (&arr)[ID_SIZE], std::index_sequence<Is...>)
: a{a},
b{b},
id{arr[Is]...}
{
}
};
int main()
{
const std::string raw_id("0123456789ABCDE");
unsigned char id[ID_SIZE];
memcpy(id, raw_id.c_str(), ID_SIZE);
conf_t conf = {10, 2048, id};
std::cout << conf.a << '\n';
std::cout << conf.b << '\n';
std::cout << conf.id << '\n';
std::cout << std::boolalpha;
std::cout << std::is_pod<conf_t>::value << '\n';
}
雖然我可能遺漏了什么,所以我歡迎任何更正。
uj5u.com熱心網友回復:
嘗試以下任一語法:
struct conf_t {
const unsigned int a;
const unsigned int b;
const unsigned char id[ID_SIZE];
};
conf_t syntax_1 = { 10, 1, { 'a', 'b', 'c' }}; // an array needs its own {}
conf_t syntax_2 = { 10, 1, "hello" }; // an array of char can be a string.
// make sure you have room for the
// null termination!
uj5u.com熱心網友回復:
初始化的錯誤是您不能用 typeconst unsigned char的左值初始化 type 的陣列元素unsigned char[16]。
你需要初始化你的成員,是使用正確的初始化語法和花括號{ vals... },并在初始化值上使用正確的型別。但是,這在標準 C 中并不那么容易,因為您正在嘗試const從一個值初始化一個值non-const。
一種不會丟失const您的id成員的解決方法是簡單地將其更改為const unsigned char*. 這里指標型別是關鍵。
然后,您可以使用 中存在的一些晦澀的怪物C ,例如reinterpret_cast<T>,并從非 const 成員初始化您的 const 成員。
#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, reinterpret_cast<const unsigned char*>(id)};
std::cout << *(conf.id);
}
現在一切都可以正常編譯了。你可以在這里看到一個活生生的例子
uj5u.com熱心網友回復:
你的宣告有些奇怪。我懷疑你們中的大多數問題都來自于濫用 const。
您的 conf_t 定義如下:
struct conf_t {
const unsigned int a;
const unsigned int b;
const unsigned char id[ID_SIZE];
};
為什么要宣告常量成員?為什么它們未初始化?它們都被宣告為 const 而沒有一個被初始化的事實讓我懷疑代碼有很大的味道。如果您需要一個常量 conf_t,則以通常的方式宣告它,如:
struct conf_t {
unsigned int a; // note that the const keyword has disappeared.
unsigned int b;
unsigned char id[ID_SIZE];
};
// the conf_t variable is declared const..
const conf_t my_conf { 10, 2048, "0123456789ABCDE" };
// et voilà !
如果您需要更大的靈活性(即:從左值初始化 id),您可以按照@Frodyne 的建議定義一個建構式,或者創建一個初始化函式,這個解決方案的優點是保持簡單的 conf_t 結構的 POD 屬性。
例子:
struct conf_t {
unsigned int a;
unsigned int b;
unsigned char id[ID_SIZE]; // is there a VERY good reason why this is unsigned char?
};
// initializes a conf_t, I guess you cpould call that a
// pseudo-constructor of sorts.
conf_t make_conf(int a, int b, const char* id)
{
// our contract:
assert(id != NULL && strlen(id) < ID_SIZE); // or strlen(id) == ID_SIZE - 1, if you wish
conf_t result = { a, b };
strncpy((char*)result.id, id, sizeof(result.id));
result.id[sizeof(result.id) - 1] = 0;
// all c compilers less than 20 years old will return in-place
return result;
}
// declaration from literals then becomes:
const conf_t cfg1 = make_conf(1, 2, "0123456789ABCDE");
// declaration from l-values becomes:
int x = 123;
int y = 42;
std::string s = "EDCBA9876543210";
const conf_t cfg2 = make_conf(x, y, s.c_str());
這不應該對手指和眼睛造成太大負擔。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/534092.html
標籤:C 数组结构
上一篇:C if(system("CLS"){system("clear)}是如何作業的
下一篇:這個程式是如何構建的?[復制]
