C/C++动态内存 or 柔性数组——对动态内存分配以及柔性数组的概念进行详细解读(张三 or 李四)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++动态内存 or 柔性数组——对动态内存分配以及柔性数组的概念进行详细解读(张三 or 李四)相关的知识,希望对你有一定的参考价值。
前言(栈区、堆区、静态区)
请耐心看完,看完后就会对内存中的空间划分有了更深刻的认识!
我们知道,任何一个变量的创建都会向内存申请空间用来存放,而在内存中的空间又划分为几个区域、最主要划分为:栈区、堆区、静态区
而我们平常创建变量或者数组,如下:
int a=0;
int arr[1000];
这里的a与arr都是在栈区开辟空间的,而栈区的特点之一就是出了作用域就会自动销毁
,所以它们的生命周期只要出了所在的作用域就结束了。因此在栈区上开辟空间的变量一般都是:局部变量、形参
这种
而且我们发现,在栈区上开辟空间的一些变量,它们的大小都是固定的
,就比如上文的数组arr,它的大小就是固定的4000字节,但是我们可以想一下,有时候在使用它的时候,并不需要这么多的空间,可能仅仅只需要10个整形大小的空间,而后面的990个整形空间都会被浪费掉,着实是可惜呀!
那我们不禁美滋滋的会这么想象,会不会存在我们想用多少空间,就开辟多少空间的可能呢?答案是有的!
我们上面提到了内存中还划分有堆区,而堆区的特点之一就是:可以按自己的需求开辟空间,并且该空间出了作用域不会自动销毁,只能人工销毁
,这就实现了我们想要的需求。
那么应如何在堆区开辟空间呢?这里就涉及到了以下讲到的几个函数:malloc、realloc、calloc,还有用来释放空间的free
可能有人还会疑问,上面的静态区是干嘛的,所谓的静态区,它的特点是:永恒存在、生命周期一直到程序结束所以在静态区开辟空间的变量一般为:常量、const修饰的常变量、全局变量、以及static修饰的静态 全局/局部 变量。
动态内存函数
我们上面已经讲过了,动态内存分配是在堆区完成、并且空间是由程序员自己释放,因此切记,malloc、calloc、realloc与free都是成对出现的
!
malloc与free
首先是malloc,向内存申请size字节的空间,然后返回该空间的起始地址。
使用演示
#include<stdio.h>
#include<stdlib.h>//头文件
int main()
int* p = (int*)malloc(10*sizeof(int));
//开辟10个整形大小的空间(40byte),然后用指针p来接收该空间的起始地址
//因为p是int*类型的,所以将该空间强制类型转换成(int*),保证用来接收的指针类型与开辟空间的类型一致
if (p == NULL)
perror("malloc");
return 1;
//对空间进行一个判断,假如开辟失败,打印错误,并返回。return 1表示非正常返回、
//开辟成功正常使用
//...
free(p);//使用完一定记得释放!(从哪里申请,从哪里释放,后面会将注意事项)
p = NULL;//将指针置空
return 0;
这里一定要对p进行判断,因为假如空间开辟失败,p就是一个空指针,后面假如对p进行操作与使用,很可能会出现很大的问题!
calloc与free
calloc与malloc很像,使用也基本相同,只不过它是这样使用的:开辟num个大小为size的空间,并且将空间的每个字节都初始化为0,而malloc开辟的空间里面的值是随机值。
使用演示
#include<stdio.h>
#include<stdlib.h>//头文件
int main()
int* p = (int*)calloc(10,sizof(int));
//开辟个10个空间,每个空间大小为一个整形大小(一共40byte),然后用指针p来接收该空间的起始地址
//因为p是int*类型的,所以将该空间强制类型转换成(int*),保证用来接收的指针类型与开辟空间的类型一致
if (p == NULL)
perror("calloc");
return 1;
//对空间进行一个判断,假如开辟失败,打印错误,并返回。return 1表示非正常返回、
//开辟成功正常使用
//...
free(p);//使用完一定记得释放!(从哪里申请,从哪里释放,后面会将注意事项)
p = NULL;//将指针置空
return 0;
realloc与free
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。
realloc 函数就可以做到对动态开辟内存大小的调整
。
但是会存在原地扩容和异地扩容两种情况
使用演示
#include<stdlib.h>//头文件
#include<stdio.h>
int main()
int* p = (int*)malloc(40);//开辟40byte
//判断是否开辟成功
if (p == NULL)
perror("malloc fail");
return 1;
//使用p指向的空间
for (int i = 0; i < 10; i++)
*(p + i) = i;
for (int i = 0; i < 10; i++)
printf("%d ", *(p + i));
//0 1 2 3 4 5 6 7 8 9
//扩容,用ptr接收新空间起始地址
int* ptr = (int*)realloc(p, 80);
if (ptr != NULL)
//扩容成功后,让p指向ptr,ptr置空
p = ptr;
ptr = NULL;
//使用
for (int i = 0; i < 20; i++)
*(p + i) = i;
for (int i = 0; i < 20; i++)
printf("%d ", *(p + i));
//使用完释放
free(p);
p = NULL;
return 0;
常见的动态内存错误
我们在使用动态内存分配时总是难免会犯一些不必要的错误,毕竟人非圣贤,孰能无过,接下来我将列举这些常见的错误,以警示避免! 1、对空指针的解引用 例:
void test()
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题,error!
free(p);
2、对动态开辟空间的越界访问 例:
void test()
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
exit(EXIT_FAILURE);
for(i=0; i<=10; i++)
*(p+i) = i;//当i是10的时候越界访问,error!
free(p);
3、对非动态开辟内存使用free释放 例:
void test()
int a = 10;
int *p = &a;
free(p);//error!
4、使用free释放一块动态开辟内存的一部分 例:
void test()
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置,error!
5、 对同一块动态内存多次释放 例:
void test()
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
6、 动态开辟内存忘记释放(内存泄漏) 例:
void test()
int *p = (int *)malloc(100);
if(NULL != p)
*p = 20;
//忘记释放!error!
int main()
test();
while(1);
动态开辟的内存空间不使用的时候一定要记得释放!
经典笔试题(再见张三)
接下来通过一些经典笔试题的讲解来加深对动态内存分配的理解: 题目一:解释运行Test函数出现的结果
void GetMemory(char *p) p = (char *)malloc(100); void Test(void) char *str = NULL; GetMemory(str, 100); strcpy(str, "hello"); printf(str);
分析:
在这里,str首先置空,把str传过去,用指针p来接收,然后p再指向新开辟的空间,再把hello拷贝到该空间,接着打印。 听起来好像没什么毛病,但是我们忽略了以下几点!首先,malloc开辟的空间并没有free,造成内存泄漏,这时最明显的错误! 然后,GetMemory这里只是传址调用,也就是说,p确实指向了那块空间,但是实际上str并没有指向,这里只是把str=NULL的值,传了过去,p=NULL,然后对p进行操作,我们知道,传值调用,形参的改变不会影响实参!所以str仍是NULL,而strcpy一个空指针,就涉及到了对空指针的解引用,ERROR! 这两处错误最为致命!
作为修改,我们可以这样改正:
void GetMemory(char** p)//一级指针的地址用二级指针来接收
*p = (char*)malloc(100);//*p 等价于*&str,等价于str,即str=......
void Test(void)
char* str = NULL;
GetMemory(&str);//传址调用,对形参的修改会影响实参
strcpy(str, "hello");
printf(str);
free(str);//释放
str = NULL;
笔试题二:以下代码运行结果:
#include<stdlib.h> #include<stdio.h> #include<string.h> char* Getmemory() char p[] = "hello world!"; return p; void test() char* str = NULL; str = Getmemory(); printf(str); int main() test(); return 0;
看起来没什么问题,str来接收Getmemory返回的地址,然后打印,按理来说应该是hello world!,但是,真实结果却是一堆乱码
这是为什么呢? 分析:
在前言那块,讲到了栈区的特点就是出作用域后会自动销毁,我们看这里的p,p是数组名,表示数组首元素的地址,在这里即字符’h‘的地址,然后返回该地址用str来接收,但是!别忘记p是个局部变量,局部变量在栈区开辟空间,出作用域后会自动销毁!也就是说虽然传给了h所在的地址,但是当它传过去的那一刻,p所在的空间就自动销毁,而str依然记着那块空间,但是此时的那块空间已经不属于p了,这就造成了野指针的访问,谁也不知道那块空间销毁后里面是什么,所以打印乱码。
图解:
张三 and 李四
举个张三与李四的故事来方便大家理解(
动态内存管理(动态内存函数的介绍,c/c++经典笔试题,柔性数组)
🎉🎇🎆👀😄❤️👍💪
🎓为什么存在动态内存分配
常见的创建变量,开辟空间,分配内存的方式如下:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
✏️上面的方式称为静态开辟,它有如下几个特点:
- 空间开辟大小是固定的。
- 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了,
这时候就只能试试动态内存开辟了。
如通过下面的简单例子,你就明白,下面是一个简略的未完善的代码:
假设我创建一个学生结构体数组,结构体成员包括姓名,性别,年龄,假设我首先静态的给数组开辟100个元素空间,之后向里面又添加元素,但慢慢的里面的元素个数会超过我们定义的最大元素个数,可能之后造成数组越界,不够用等情况,所以这种情况我们可以动态的开辟数组空间,够了就用,不够继系开辟即可。
#include<stdio.h>
#define MAX 1000//初始大小,100个学生
#define ADD 5//增加5个学生
struct student
{
char name[20];
char sex[4];
int age;
}student;
//静态版本
//struct student data[MAX];
//动态版本
struct student *data;
void add(struct student* pc)
{
data = (struct student*)malloc((MAX+ADD) * sizeof(struct student));
if (data == NULL)
{
perror("InitContact");
return;
}
//.............................
//.............................
}
int main()
{
add(data);
return 0;
}
🎓动态内存函数的介绍
✏️malloc和free
这两个函数总是成对出现的,一个开辟内存,一个释放内存,这两个函数的单独使用极有可能会导致程序出错。
动态内存开辟的函数malloc
函数原型 void* malloc (size_t size);
函数说明
- 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。 如果开辟成功,则返回一个指向开辟好空间的指针
- size_t size表示开辟几个字节大小的空间
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- 如果参数 size为0,malloc的行为是标准是未定义的,取决于编译器。
动态内存释放函数free
函数原型 void free (void* ptr);
函数说明
- -ptr 传过来的是开辟空间的起始地址, 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的,如果为空指针什么也不会发生
- 如果参数 ptr 是NULL指针,则函数什么事都不做。 free函数释放内存空间后,并不会将接受开辟空间起始地址的的指针置为空指针
malloc和free都声明在 stdlib.h 头文件中,接下来我举一个开辟内存释放内存的例子:
#include <stdlib.h>
int main()
{
//动态内存开辟的
int* p = (int*)malloc(10*sizeof(int));//void*
//使用这些空间的时候首先判断空间是否开辟成功
if (p == NULL)
{
perror("main");//如果在main函数里面,开辟空间失败,通过perror函数打印错误信息
return 0;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;//通过指针加减整数的方式赋初值
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));//p[i] --> *(p+i)这两种访问方式等价的,p指向开辟的那块空间的起始地址,
//相当于数组名通过数组名加下标的方式访问访问开辟的空间
}
//回收空间
free(p);///free函数释放P指向的内存空间,但不会把p指针里面地址的内容释放,这可能就会造成,p又通过地址访问之前的内存空间,造成内存非法访问
p = NULL;//所以自己动手把p置为NULL
return 0;
}
💏💏这里有几个细节的地方学要注意:
malloc开辟空间后,free函数释放P指向的内存空间,但不会把p指针里面地址的内容释放,这可能就会造成,p又通过地址访问之前的内存空间,造成内存非法访问,所以一定要手动的把把P置为NULL
✏️calloc
C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。
函数原型如下: void* calloc (size_t num, size_t size); 函数说明:
- 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
- 与函数 malloc 的区别只在于calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
举个例子我们使用calloc开辟10个整形空间的大小
#include <stdlib.h>
int main()
{
//int*p = (int*)malloc(40);
int* p = calloc(10, sizeof(int));
if (p == NULL)
return 1;//如果为空直接结束后面的执行
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\\n", *(p + i));
}
free(p);
p = NULL;
return 0;
}
打印calloc开辟空间里面的内容如图:
所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。
✏️realloc
realloc函数的出现让动态内存管理更加灵活。有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理地管理内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
函数原型如下: void* realloc (void* ptr, size_t size);
函数说明:
- 这个函数可以在原有的其它内存函数开辟空间的基础上,继系管理空间的大小,也可以自己重新开辟一块新的内存空间,开辟空间时不初始化里面的内容。
- ptr 是要调整的内存地址(原内存的起始地址), size 为调整之后内存空间的新大小 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间时,如果原内存空间后面有足够的空间则开辟相应的空间,如果原内存空间后面没有足够的空间可以开辟,就在堆区重新找一块空间开辟内存,之后还会将原来内存中的数据移动到新 的空间。
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间
以下面的的代码为例,下图分析两种情况:
情况一:
情况二:
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(3, sizeof(int));//开辟3个大小的整形空间
if (p == NULL)
{
perror("main");
return 1;
}
//如果这里需要p指向的空间更大,需要6个int的空间
//realloc调整空间
int*ptr = (int*)realloc(p, 6*sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
//使用
int i = 0;
for (i = 0; i < 6; i++)
{
printf("%d ", *(p + i));
}
//回收空间
free(p);
p = NULL;
return 0;
}
✏️当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。当是情况2 的时候,如果原内存空间后面没有足够的空间可以开辟,就在堆区重新找一块空间开辟内存,之后还会将原来内存中的数据移动到新的空间。
🎓常见的动态内存错误
👀对NULL指针的解引用操作
空指针就是没有任何指向的指针,不能对它进行解引用,加减整数等操作,任何对它的操作都是不和法的,都会造成程序的崩溃。malloc函数在开辟内存失败时,会返回空指针,所以在对malloc函数开辟的空间进行使用之前要判断是否为返回空指针。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int* p = (int*)malloc(10000000000000000);
//开辟失败,返回空指针没有判断就使用
//使用
int i = 0;
for (i = 0; i < 6; i++)
{
printf("%d ", *(p + i));
}
}
👀对动态开辟空间的越界访问
其实这个很好理解,就像静态的创建10个数组元素,你可不能访问20个元素啊;如下面的代码,malloc动态的开辟了10整形大小的空间,下面使用空间时却访问了40个整形元素,这也是不合法的。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
int i = 0;
//越界访问
for (i = 0; i < 40; i++)
{
*(p + i) = i;
}
free(p);
p = NULL;
return 0;
}
🔧🔩对非动态开辟内存使用free释放
通过什么方式创建,就要通过什么方式释放
int main()
{
int arr[10] = { 0 };//栈区
int* p = arr;
//使用
free(p);//使用free释放非动态开辟的空间
p = NULL;
return 0;
}
✏️使用free释放一块动态开辟内存的一部分free(p),p一定要指向开辟空间的起始位置,这样才能释放开辟的整块动态空间,如果p因为使用原因进行了移动一定要定义另一个指针记录p的开始指向位置,否则进行的内存释放是局部的内存释放。下面代码中通过指针的移动对空间内容进行了赋值,p发生了移动,但没有记录p的起始位置。
int main()
{
int* p = malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*p++ = i;
}
free(p);
p = NULL;
return 0;
}
🎸🎧对同一块动态内存多次释放
对于开辟的一块动态空间,一次释放就行,但有时由于程序的复杂,在多个函数里面使用这块空间,也就可能会进行多次释放。
int main()
{
int* p = (int*)malloc(100);
//使用
//释放
free(p);
p = NULL;
test()
{
//释放
free(p);
return 0;
}
}
👻🤡动态开辟内存忘记释放(内存泄漏)
如下p是局部变量,出了这个函数就销毁了,下面的main 函数里面就不能使用了,之后就找不到p原来指向的空间的内容了,下面就不能把这块空间释放掉,造成内存泄漏,时间一长会消耗很多内存,如果服务器里面有内存泄漏,导致这个服务器崩溃。
void test()
{
int* p = (int*)malloc(100);
if (p == NULL)//p是局部变量,出了这个函数就销毁了
{
return;
}
else
{
//使用
}
//一块空间开辟了自己用,用完了一定要记得释放,这里不释放,下面就不能把这块空间释放掉,造成内存泄漏
}
int main()
{
test();//函数调用
//....
return 0;
}
🎓几个经典的笔试题
📝题目一:
首先思考这个代码有哪些问题??
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
return 0;
}
这个代码有两个问题:
❌1. 对空指针的使用
❌ 2. 存在内存泄漏
图示解释📐✏️✂️
✏️代码修改的两种方法
✏️在函数调用时,传值调用是无法改变实参的大小的,要传地址。
//改1
void GetMemory(char** p)//用二级指针来接收
{
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);//传一级指针的地址
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
//改:2
//char* GetMemory(char* p)//改变函数的返回值,返回动态开辟空间的起始地址(char*)
//{
// p = (char*)malloc(100);
// return p;
//}
//void Test(void)
//{
// char* str = NULL;
// str = GetMemory(str);
// strcpy(str, "hello world");
// printf(str);//?
// //printf("hello world");//char *p = "hello world";
// free(str);
// str = NULL;
//}
//int main()
//{
// Test();
// return 0;
//}
📝题目二:
❌静态开辟的空间是在栈上,栈上开辟的空间出了作用域就销毁了,动态开辟的空间是在堆上开辟的,要么自己手动释放空间,要么程序结束自动释放空间。所以下面的代码,随机打印内存中的一些值,没有达到预想到的效果。
//GetMemory 函数内部创建的数组是在栈区上创建的
//出了函数,p数组的空间就还给了操作系统
//返回的地址是没有实际的意义,如果通过返回的地址,去访问内存就是非法访问内存的
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);//没有返回确定的地址,随机打印内存中的一些值
}
📝题目三
❌返回&x是无效的,局部变量返回地址是无效的
int test (void)
{
int x = 10;
return (&x);
}
📝题目四
❌局部变量不初始化,随机赋值,导致指向不明确,解引用有问题,野指针问题。
int test(void)
{
int *x;
*x = 10;
return x;
}
📝题目五
❌开辟空间没有free释放掉
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
return 0;
}
📝题目六
❌动态空间开辟好后,free释放开辟的空间,那么维护开辟空间起始位置的指针也应该置为空指针,这是紧紧相连的步骤,不可缺少。虽然free(str)释放了开辟好的空间,但str里面任然存储着开辟空间的起始地址,free不会释放strl里面的内容的,导致非法访问,导致程序错误。
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
//这里可以加上str=NULL就对了
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
🎓 C/C++程序的内存开辟
💡C/C++程序内存分配的几个区域如下图:
不同的变量,程序在内存中占有不同的区域,理解他们所在的区域,理解他们的作用域与生命周期,可以帮助我们更好地编写程序。
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有
限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。 - 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似
于链表。 - 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
编译产生的可执行代码(可执行程序)是放在代码段区域的,常量字符串
也是放在代码段里面的。
💡有了这幅图,我们就可以更好地理解static关键字修饰的局部变量了
实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长。
🎓 柔性数组
柔性数组(flexible array)这个概念我们很少听到,但是它确实是存在的。 C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。我自己对于柔性数组的理解就是,柔性数组是定义在结构体当中的一个成员,它的起始大小为零,在使用过程中,根据情况的需要,通过动态内存开辟函数改变它的大小,达到数组内容改变的效果。
如:
struct S
{
int n;
int arr[];//大小是未知
// int arr[0];//大小是未知,这种柔性数组的写法也对,大小未知,不是柔性数组,这个写法就是非法的
};
✏️柔性数组的特点:
- 结构体中的柔性数组成员前面必须至少一个其他成员。
- sizeof 返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应 柔性数组的预期大小。
✏️ 如下代码进行结构体的大小的计算:
#include<stdio.h>
struct s
{
int i;
int a[];//柔性数组成员
}a;
int main()
{
printf("%d\\n", sizeof(struct s ));//输出的是4
return 0;
}
下面为柔性数组的简单使用:
代码一
首先数组元素个数为10个,不够时再动态的开辟为20个
#include<stdio.h>
struct S
{
int n;//4
int arr[0];//大小是未知
};
int main()
{
//期望arr的大小是10个整形
struct S*ps = (struct S*)malloc(sizeof(struct S)+10*sizeof(int));
ps->n = 10;
浅谈C语言的动态内存开辟
动态内存管理(动态内存函数的介绍,c/c++经典笔试题,柔性数组)