如何在结构中放置可变大小的 char 数组?

Posted

技术标签:

【中文标题】如何在结构中放置可变大小的 char 数组?【英文标题】:How do I fit a variable sized char array in a struct? 【发布时间】:2010-06-26 10:14:38 【问题描述】:

我不明白结构的内存重新分配如何允许我将更大的 char 数组插入到我的结构中。

结构定义:

typedef struct props

    char northTexture[1];
    char southTexture[1];
    char eastTexture[1];
    char westTexture[1];
    char floorTexture[1];
    char ceilingTexture[1];
 PROPDATA;

示例:

void function SetNorthTexture( PROPDATA* propData, char* northTexture )

    if( strlen( northTexture ) != strlen( propData->northTexture ) )
    
        PROPDATA* propPtr = (PROPDATA*)realloc( propData, sizeof( PROPDATA ) +
            sizeof( northTexture ) );
        if( propPtr != NULL )
        
            strcpy( propData->northTexture, northTexture );
         
    
    else
    
        strcpy( propData->northTexture, northTexture );
    

我已经测试了类似的东西,它似乎工作,我只是不明白它是如何工作的。现在我希望有些人在想“只使用 char*”,但无论出于何种原因,我都做不到。字符串必须存储在结构本身中。

我的困惑来自于我没有为任何特定目的调整结构的大小。在该示例中,我没有以某种方式表明我希望将额外的空间分配给北纹理字符数组。我想我分配的额外内存用于实际存储字符串,并且当我调用 strcpy 时,它意识到没有足够的空间......

任何关于它是如何工作的(或者它是如何存在缺陷的)的解释都会很棒。

【问题讨论】:

欢迎来到 Stack Overflow!为了将来参考,我们通过在每行前面添加四个空格来格式化代码。对于代码示例,您不需要使用 <br>[code]。 (请注意,Ctrl+K 或在所选文本的格式工具栏上按“101010”按钮与在每行前添加四个空格相同)。 当你说“但我不能出于任何原因”时,你是在暗示这是一个假设的练习吗?如果这是假设性的,那么下面的答案清楚地为您提供了选择。如果您有真正的应用需求,请告诉我们它是什么。退后一步可能会为您提供能够大大改进您的整体设计的建议。 回答您的问题标题:这实际上是不可能的,但是您可以给用户一种可以实现的错觉。请参阅下面的答案。 【参考方案1】:

这是 C 还是 C++?您发布的代码是 C,但如果它实际上是 C++(如标签所示),则使用 std::string。如果是 C,那么有两种选择。

如果(如您所说)您必须将字符串存储在结构本身中,那么您无法调整它们的大小。 C 结构根本不允许这样做。 “大小为 1 的数组”技巧有时用于将单个可变长度字段固定到结构的末尾,但不能在其他任何地方使用,因为每个字段在结构中都有固定的偏移量。您能做的最好的事情就是确定一个最大大小,并让每个数组都具有该大小。

否则,将每个字符串存储为char*,并使用realloc 调整大小。

【讨论】:

+1 用于在结构约定结束时提及大小为 1 的数组,以及 c++ 和 c 的正确后备【参考方案2】:

这个答案不是为了推广下面描述的做法,而是为了解释事情。有很好的理由不使用 malloc,并且在其他答案中使用 std::string 的建议是有效的。

我认为您已经遇到了例如 Microsoft 使用的技巧来获取指针取消引用的成本。对于Unsized Arrays in Structures(请查看链接),它依赖于语言的非标准扩展。即使没有扩展名,您也可以使用这样的技巧,但仅适用于位于内存末尾的结构成员。通常结构声明中的最后一个成员也是内存中的最后一个,但请查看question 以了解更多信息。为了使技巧起作用,您还必须确保编译器不会在结构的末尾添加填充字节。

大致思路是这样的:假设你有一个结构体,末尾有一个数组

struct MyStruct

    int someIntField;
    char someStr[1];
;

在堆上分配时,你通常会这样说

MyStruct* msp = (MyStruct*)malloc(sizeof(MyStruct));

但是,如果您分配的空间比您的结构实际占用的空间多,您可以引用布局在内存中的字节,就在结构后面,对数组元素进行“越界”访问。 假设 int 和 char 的一些典型大小,并且末尾缺少填充字节,如果您这样写:

MyStruct* msp = (MyStruct*)malloc(sizeof(MyStruct) + someMoreBytes);

内存布局应如下所示:

|    msp   |   msp+1  |   msp+2  |   msp+3  |   msp+4  |   msp+5  |   msp+6  | ... |
|    <-         someIntField         ->     |someStr[0]|  <-   someMoreBytes  ->   |

在这种情况下,您可以像这样引用地址 msp+6 处的字节:

msp->someStr[2];

【讨论】:

关于数组作为结构的最后一个元素的一个很好的参考:gcc.gnu.org/onlinedocs/gcc/Zero-Length.html【参考方案3】:

strcpy 没有那么聪明,而且它并没有真正起作用。

对 realloc() 的调用为字符串分配了足够的空间 - 所以它实际上并没有崩溃,但是当你将字符串 strcpy 到 propData->northTexture 时,你可能会覆盖 propData 中的 northTexture 之后的任何内容 - propData->southTexture, propData ->westTexture 等

例如你叫SetNorthTexture(prop, "texture"); 并打印出不同的纹理,那么您可能会发现:

 northTexture is "texture"
 southTexture is "exture"
 eastTexture is "xture" etc (assuming that the arrays are byte aligned). 

假设您不想静态分配足够大的 char 数组以容纳最大的字符串,并且如果您绝对必须在结构中包含字符串,那么您可以将字符串一个接一个地存储在结构的末尾。显然,您将需要动态地分配您的结构以有足够的空间来保存所有字符串 + 到其位置的偏移量。

这是非常混乱和低效的,因为如果添加、删除或更改字符串,您需要随机处理。

【讨论】:

啊,好的。那不好。这实际上是我所期望的情况。关于如何正确调整大小的任何想法?【参考方案4】:

我的困惑来自于这样一个事实 我还没有调整我的结构的大小 特定目的。

在像 C 这样的低级语言中,结构(或一般类型)和实际内存之间存在某种区别。分配基本上包括两个步骤:

    分配正确大小的原始内存缓冲区 告诉编译器这段原始字节应该被视为一个结构

执行 realloc 时,不会更改结构,但会更改存储它的缓冲区,因此您可以使用结构之外的额外空间。

请注意,尽管您的程序不会崩溃,但它是不正确的。将文本放入northTexture时,会覆盖其他结构字段。

【讨论】:

【参考方案5】:

注意:这里没有 char 数组示例,但原理相同。这只是我对您要达到的目标的猜测。

我的看法是你见过somewhere something like this:

typedef struct tagBITMAPINFO 
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
 BITMAPINFO, *PBITMAPINFO;

只有当数组位于结构的末尾(并且只有一个数组)时,才会发生您试图获取的内容。

例如,当您需要存储 16 个 RGBQUAD 结构(1 个来自结构,15 个额外)时,您分配 sizeof(BITMAPINFO)+15*sizeof(GBQUAD)

PBITMAPINFO info = (PBITMAPINFO)malloc(sizeof(BITMAPINFO)+15*sizeof(GBQUAD));

您可以像在 BITMAPINFO 结构中一样访问所有 RGBQUAD 结构:

info->bmiColors[0]
info->bmiColors[1]
...
info->bmiColors[15]

您可以执行类似于在结构末尾声明为 char bufStr[1] 的数组。

希望对你有帮助。

【讨论】:

【参考方案6】:

将结构及其所有字符串保存在一个分配的内存块中的一种方法是这样的:

struct foo 
    ptrdiff_t s1, s2, s3, s4;
    size_t bufsize;
    char buf[1];
 bar;

分配sizeof(struct foo)+total_string_size字节并将偏移量存储到s1s2等成员中的每个字符串,然后bar.buf+bar.s1是指向第一个字符串的指针,bar.buf+bar.s2是指向第二个字符串的指针等。

如果您知道不需要realloc 结构,则可以使用指针而不是偏移量。

这样做是否有意义值得商榷。一个好处是,当您拥有大量微小数据对象(尤其是在线程环境中)时,它可能有助于对抗内存碎片或 malloc/free 开销。如果您要检查单个 malloc 故障,它还可以降低错误处理清理的复杂性。确保数据局部性可能有缓存优势。并且可以(如果您使用偏移量而不是指针)将对象存储在磁盘上而无需任何序列化(请记住,您的文件是特定于机器/编译器的)。

【讨论】:

以上是关于如何在结构中放置可变大小的 char 数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用PHP在csv中放置关联数组?

在哪里/如何在 OpenCV 中放置构建文件

如何使用 JavaScript 在单元格中放置另一个文本?

关于如何在 Ios 中放置纵横比的问题。

在 C++ 中的可变长度函数中放置默认值参数的位置?

如何在 Angular Material 的对话框中放置图像?