如何在结构中拟合可变大小的char数组?

时间:2010-06-26 10:14:39

标签: c++ c arrays struct

我不明白结构的内存重新分配如何允许我在我的结构中插入一个更大的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时,它意识到没有足够的空间......

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

6 个答案:

答案 0 :(得分:9)

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

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

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

答案 1 :(得分:1)

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数组以容纳最大的字符串,并且如果你必须在结构中有字符串,那么你可以在结构的末尾一个接一个地存储字符串。显然,您需要动态malloc您的结构,以便有足够的空间来容纳所有字符串+偏移到它们的位置。

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

答案 2 :(得分:0)

  

我的困惑来自于这样的事实   我还没有调整我的结构   具体目的。

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

  1. 正确大小的原始内存缓冲区的分配
  2. 告诉编译器应该将这段原始字节视为结构
  3. 当你进行realloc时,你不会改变结构,但你改变它所存储的缓冲区,所以你可以使用超出结构的额外空间。

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

答案 3 :(得分:0)

这个答案不是为了促进下面描述的做法,而是为了解释事情。有很好的reasens不使用malloc和建议使用std :: string,在其他答案中,是有效的。

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

一般的想法是这样的:假设你有一个结构,最后有一个数组,如

struct MyStruct
{
    int someIntField;
    char someStr[1];
};

在堆上分配时,通常会说出类似这样的内容

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

但是,如果你分配了比你的stuct实际占用更多的空间,你可以引用在内存中排列的字节,在结构后面,对数组元素有“越界”访问权限。 假设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];

答案 4 :(得分:0)

注意:这没有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));

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

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

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

希望它有所帮助。

答案 5 :(得分:0)

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

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是指向第一个字符串的指针,{ {1}}指向第二个字符串的指针等。

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

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