如何在rodata中初始化灵活数组并创建指向它的指针?

加文·扬西

在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是一个灵活的数组:我得到了错误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;
}

正如我们通过查看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;
}

查看其生成的程序集,第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; \
    })

通过扩展,您可以在一个块中包含多个语句作为表达式,该表达式通过将块括在中来求值最后一条语句的值({ ... })因此,我们可以将我们PascalStringstatic const声明为一个值,然后返回指向它的指针。

为了完整起见,如果我们要修改它,我们也可以创建一个堆栈缓冲区:

#define PASCAL_STRING_STACKBUF(initial_value, capacity) \
    (PascalString *)&(struct { \
        uint32_t Length; \
        char Data[capacity]; \
    }){ \
        .Length = sizeof(initial_value)-1, \
        .Data = initial_value, \
    }

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章