如何在 rodata 中初始化灵活数组并创建指向它的指针?
How can I initialize a flexible array in rodata and create a pointer to it?
在 C 中,代码
char *c = "Hello world!";
将 Hello world![=17=]
存储在 rodata 中并使用指向它的指针初始化 c
。
我怎样才能用字符串以外的东西来做到这一点?
具体来说,我正在尝试定义自己的字符串类型
typedef struct {
size_t Length;
char Data[];
} PascalString;
然后想要某种宏,这样我就可以说
const PascalString *c2 = PASCAL_STRING_CONSTANT("Hello world!");
并使其表现相同,因为 \x0c[=19=][=19=][=19=]Hello world!
存储在 rodata 中,c2
使用指向它的指针进行初始化。
我试过使用
#define PASCAL_STRING_CONSTANT(c_string_constant) \
&((const PascalString) { \
.Length=sizeof(c_string_constant)-1, \
.Data=(c_string_constant), \
})
如 these 中所建议,但它不起作用,因为 Data
是一个灵活的数组:我收到错误 error: non-static initialization of a flexible array member
(使用 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;
}
正如我们在 the assembly 中看到的那样,第 5 行编译为仅将指针加载到寄存器中。
我希望能够使用 Pascal 字符串获得相同的行为,并且使用 GNU 扩展是可能的。以下代码也从不将 pascal 字符串存储在堆栈中:
#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;
}
查看its generated assembly,第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;
}
在 generated assembly for this code 中,第 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
值,然后 return 指向它的指针。
为了完整起见,如果我们想修改它,我们也可以制作一个堆栈缓冲区:
#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!");
我不确定你为什么要这样做,但你可以这样做。
此方法会将您的字符串存储在数据段中,并为您提供一种将其作为结构访问的方法。请注意,我创建了一个打包结构以确保映射到结构中始终有效,因为我基本上已经在下面的 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','[=10=]'
};
int main(void) {
PascalString * myString = (PascalString *)HELLO;
printf("I say: %s \n", myString->Data);
}
在 C 中,代码
char *c = "Hello world!";
将 Hello world![=17=]
存储在 rodata 中并使用指向它的指针初始化 c
。
我怎样才能用字符串以外的东西来做到这一点?
具体来说,我正在尝试定义自己的字符串类型
typedef struct {
size_t Length;
char Data[];
} PascalString;
然后想要某种宏,这样我就可以说
const PascalString *c2 = PASCAL_STRING_CONSTANT("Hello world!");
并使其表现相同,因为 \x0c[=19=][=19=][=19=]Hello world!
存储在 rodata 中,c2
使用指向它的指针进行初始化。
我试过使用
#define PASCAL_STRING_CONSTANT(c_string_constant) \
&((const PascalString) { \
.Length=sizeof(c_string_constant)-1, \
.Data=(c_string_constant), \
})
如 these Data
是一个灵活的数组:我收到错误 error: non-static initialization of a flexible array member
(使用 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;
}
正如我们在 the assembly 中看到的那样,第 5 行编译为仅将指针加载到寄存器中。
我希望能够使用 Pascal 字符串获得相同的行为,并且使用 GNU 扩展是可能的。以下代码也从不将 pascal 字符串存储在堆栈中:
#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;
}
查看its generated assembly,第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;
}
在 generated assembly for this code 中,第 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
值,然后 return 指向它的指针。
为了完整起见,如果我们想修改它,我们也可以制作一个堆栈缓冲区:
#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!");
我不确定你为什么要这样做,但你可以这样做。 此方法会将您的字符串存储在数据段中,并为您提供一种将其作为结构访问的方法。请注意,我创建了一个打包结构以确保映射到结构中始终有效,因为我基本上已经在下面的 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','[=10=]'
};
int main(void) {
PascalString * myString = (PascalString *)HELLO;
printf("I say: %s \n", myString->Data);
}