[英]How can I initialize a flexible array in rodata and create a pointer to it?
在 C 中,代碼
char *c = "Hello world!";
將Hello world!\0
存儲在 rodata 中,並使用指向它的指針初始化c
。 我怎么能用字符串以外的東西來做到這一點?
具體來說,我正在嘗試定義自己的字符串類型
typedef struct {
size_t Length;
char Data[];
} PascalString;
然后想要某種宏,這樣我就可以說
const PascalString *c2 = PASCAL_STRING_CONSTANT("Hello world!");
並讓它表現相同,在那個\x0c\0\0\0Hello world!
存儲在rodata中, c2
用指向它的指針初始化。
我嘗試使用
#define PASCAL_STRING_CONSTANT(c_string_constant) \
&((const PascalString) { \
.Length=sizeof(c_string_constant)-1, \
.Data=(c_string_constant), \
})
正如這些問題中所建議的那樣,但它不起作用,因為Data
is a flexible array: I get the error error: non-static initialization of a flexible array member
(with gcc, clang 給出了類似的錯誤)。
這在 C 中是否可行? 如果是這樣, PASCAL_STRING_CONSTANT
宏會是什么樣子?
澄清
對於 C 字符串,以下代碼塊永遠不會將字符串存儲在堆棧中:
#include <inttypes.h>
#include <stdio.h>
int main(void) {
const char *c = "Hello world!";
printf("test %s", c);
return 0;
}
正如我們通過查看程序集所看到的,第 5 行編譯為僅將指針加載到寄存器中。
我希望能夠使用 pascal 字符串獲得相同的行為,並且可以使用 GNU 擴展。 以下代碼也從不將帕斯卡字符串存儲在堆棧中:
#include <inttypes.h>
#include <stdio.h>
typedef struct {
size_t Length;
char Data[];
} PascalString;
#define PASCAL_STRING_CONSTANT(c_string_constant) ({\
static const PascalString _tmpstr = { \
.Length=sizeof(c_string_constant)-1, \
.Data=c_string_constant, \
}; \
&_tmpstr; \
})
int main(void) {
const PascalString *c2 = PASCAL_STRING_CONSTANT("Hello world!");
printf("test %.*s", c2->Length, c2->Data);
return 0;
}
查看其生成的程序集,第 18 行也只是加載了一個指針。
但是,我發現在 ANSI C 中執行此操作的最佳代碼會生成將整個字符串復制到堆棧的代碼:
#include <inttypes.h>
#include <stdio.h>
typedef struct {
size_t Length;
char Data[];
} PascalString;
#define PASCAL_STRING_CONSTANT(initial_value) \
(const PascalString *)&(const struct { \
uint32_t Length; \
char Data[sizeof(initial_value)]; \
}){ \
.Length = sizeof(initial_value)-1, \
.Data = initial_value, \
}
int main(void) {
const PascalString *c2 = PASCAL_STRING_CONSTANT("Hello world!");
printf("test %.*s", c2->Length, c2->Data);
return 0;
}
在為此代碼生成的程序集中,第 19 行將整個結構復制到堆棧上,然后生成指向它的指針。
我正在尋找生成與我的第二個示例相同的組件的 ANSI C 代碼,或者解釋為什么 ANSI C 無法實現。
這可以通過statment-expressions GNU 擴展來完成,盡管它是非標准的。
#define PASCAL_STRING_CONSTANT(c_string_constant) ({\
static const PascalString _tmpstr = { \
.Length=sizeof(c_string_constant)-1, \
.Data=c_string_constant, \
}; \
&_tmpstr; \
})
該擴展允許您在一個塊中擁有多個語句作為表達式,該表達式通過將塊括在({... })
中來計算最后一條語句的值。 因此,我們可以將PascalString
聲明為static const
量值,然后返回指向它的指針。
為了完整起見,如果我們想修改它,我們也可以創建一個堆棧緩沖區:
#define PASCAL_STRING_STACKBUF(initial_value, capacity) \
(PascalString *)&(struct { \
uint32_t Length; \
char Data[capacity]; \
}){ \
.Length = sizeof(initial_value)-1, \
.Data = initial_value, \
}
您可以使用此宏,它在其內容上命名變量的名稱:
#define PASCAL_STRING(name, str) \
struct { \
unsigned char len; \
char content[sizeof(str) - 1]; \
} name = { sizeof(str) - 1, str }
創建這樣的字符串。 像這樣使用它:
const PASCAL_STRING(c2, "Hello world!");
答案是不,你不能初始化一個靈活的數組 in.rodata 並在普通的 C 中創建一個指向它的指針。
這有幾個原因; 作為起點,標准 C 沒有指定.rodata
部分。 另一個原因是類似的東西可以用指針幾乎等價地實現。
有很多解決方案,包括分配 memory 和malloc
,使用(有點)固定大小的Data
數組,或使用語句表達式,但您已經排除了這些(因為它們不會將結果存儲在.rodata
中(也就是他們將它存儲在堆棧中)或者他們使用 GNU 擴展)。 因此,任何便攜式解決方案都無法完全滿足您的需求。
C 標准規定您不能在 ISO/IEC 9899:1999 第 6.7.2.1 節第 18 點中初始化靈活數組成員:
struct s { int n; double d[]; };
[...]
struct s t2 = { 1, { 4.2 }}; // invalid
[...]
t2的初始化是無效的(並且違反了約束),因為struct s被視為不包含成員d 。
[...]
然而,它不能出現在嚴格符合的代碼中。
因此,澄清一下:標准 C 沒有指定您希望能夠更改的這些概念(堆棧、rodata、組件)。 因此,除非您有一個允許您更改這些內容的編譯器(*cough* GCC),否則您無法更改它們。 只要有效程序(沒有實現定義的、未指定的或未定義的行為)以相同的方式運行,編譯器就可以完全自由地更改它想要的任何內容。
這是評論而不是答案(因為我沒有足夠的聲譽來評論這個問題)。 我只是好奇為什么這不起作用。
typedef struct {
const char *data;
unsigned char len;
} PascalString;
const PascalString s = { "new string", strlen("new string")};
我不確定你為什么要這樣做,但你可以這樣做。 此方法會將您的字符串存儲在數據段中,並為您提供一種將其作為結構訪問的方法。 請注意,我創建了一個打包結構以確保映射到該結構中始終有效,因為我基本上已經在下面的 const 表達式中對數據字段進行了硬編碼。
#include <stdio.h>
#pragma packed(1)
typedef struct {
unsigned char Length;
char Data[];
} PascalString;
#pragma pack()
const unsigned char HELLO[7] = {
0x06,
'H','E','L','L','O','\0'
};
int main(void) {
PascalString * myString = (PascalString *)HELLO;
printf("I say: %s \n", myString->Data);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.