C语言如何定义一个可变大小的数组

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言如何定义一个可变大小的数组相关的知识,希望对你有一定的参考价值。

int a ;
scanf("%d",&a);
char cha[a][3];
这样定义为什么不行呢?如果把第一行换成int a = 1;也是不行,望大神指教。

在C语言中,数组的大小是在定义的时候就需要确定的,不然编译的时候进行分配内存时,会使计算机不知道该分配多少内存单元而报错。


然而,可以根据定义一个链表来达到可变的效果。

链表节点定义如下:

struct

    int data;   // 数据域
    struct node *next;   // 指针域
 *node;

struct node head, p;
head->data = 4;
head->next = p;
p->data = 5;

类似的可以将一个节点看成是数组中的一个元素,需要增加元素的时候,就增加链表的节点就可以了。

参考技术A 可变长数组是C99标准中加入的,你的编译器不能用,只能说明不支持C99的这一特性本回答被提问者和网友采纳 参考技术B 这样定义是错误的。
因为数组的长度里要填入常数。而scanf输入、int定义都是变量。
用#define a 1,定义常量。

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

【中文标题】如何在结构中放置可变大小的 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 故障,它还可以降低错误处理清理的复杂性。确保数据局部性可能有缓存优势。并且可以(如果您使用偏移量而不是指针)将对象存储在磁盘上而无需任何序列化(请记住,您的文件是特定于机器/编译器的)。

【讨论】:

以上是关于C语言如何定义一个可变大小的数组的主要内容,如果未能解决你的问题,请参考以下文章

C语言中,如何将一个数组中的数值转换成字符串输出?

在C#中怎么使数组是个可变的,数组大小由用户自己输入

C语言实现一个可变长的二维数组

c语言动态数组如何扩充空间

C语言如何定义一个动态数组?

c语言中如何定义动态指针数组