簡體   English   中英

如何在 rodata 中初始化一個靈活的數組並創建一個指向它的指針?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM