【C语言进阶】之动态内存管理笔试题及柔性数组

【C语言进阶】之动态内存管理笔试题

  • 1.动态内存管理笔试题汇总
    • 1.1第一道题
    • 1.2第二道题
    • 1.3第三道题
    • 1.4第四道题
  • 2.C/C++内存管理
  • 3.柔性数组
    • 3.1什么是柔性数组
    • 3.2柔性数组的使用
    • 3.2柔性数组的优点

博客主页: 小镇敲码人
欢迎关注:点赞 留言 收藏
任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。
❤️ 什么?你问我答案,少年你看,下一个十年又来了
前言:接上一篇博客【C语言进阶】之动态内存管理,今天来跟着博主把理论应用在实践之中,彻底掌握动态内存管理的相关知识!!!!

1.动态内存管理笔试题汇总

1.1第一道题

char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}

请问运行Test函数会有怎样的结果呢?
运行结果:

【C语言进阶】之动态内存管理笔试题及柔性数组_第1张图片

解析:数组p是函数里开的一个临时变量,它的空间开在栈区上,出了GetMemory函数作用域它的生命周期结束,系统就把它的空间回收了,所以你返回的地址是系统已经回收的地址,里面的内容是未知的,是一串字符。

另外printf打印字符串,可以直接传字符串首元素的地址打印。

1.2第二道题

void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}

运行结果:

【C语言进阶】之动态内存管理笔试题及柔性数组_第2张图片
解析:程序直接崩溃,这是为什么呢?明明这次已经在堆区开辟空间了呀,实际上,我们并没有改变str,如果你不相信,我们可以来验证一下:

【C语言进阶】之动态内存管理笔试题及柔性数组_第3张图片
在调用函数GetMemory后为什么str的值没有改变呢?因为str和p都是一级指针,它们两个唯一的相似之处就是它们的值是一样的,本质还是值传递,想改变一级指针的值,需要传它的地址,我们把代码这样改就对了:

#include
#include
#include
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;
}

【C语言进阶】之动态内存管理笔试题及柔性数组_第4张图片

1.3第三道题

void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}

请问运行Test 函数会有什么样的结果?

运行结果:
【C语言进阶】之动态内存管理笔试题及柔性数组_第5张图片
这道题和上一道题我们修改后的类似不做过多阐述,但是有个问题,虽然编译器没有报错,但是它动态开辟的空间没有释放,会造成内存泄露。

1.4第四道题

void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}

请问运行Test 函数会有什么样的结果?
运行结果:

【C语言进阶】之动态内存管理笔试题及柔性数组_第6张图片
解析:
虽然运行结果没有问题,但其实这段代码是不对的,原因就是我们在free动态开辟的空间后没有及时的去将str置为空指针,使他成为了一个野指针,那片地址已经不属于我们了,你还进行写入和访问,显然是非法的,所以我们应该养成好习惯,free后就要将指针变量置空。

2.C/C++内存管理

【C语言进阶】之动态内存管理笔试题及柔性数组_第7张图片

C/C++内存分配的几个区域:

1.栈区(stack):栈区主要放一些函数中创建的局部变量,生命周期在函数结束时就结束了,内存也被系统(OS)回收,这个区域通常存放临时变量、函数的参数、函数的返回数据、返回地址等。读写效率高,但是容量有限。
2.堆区(heap):这个区域主要是动态开辟的内存存放的地方,通常只有程序结束时系统才会回收其内存,除非你手动使用去回收,像C语言中使用的free函数。
3.数据段(静态区 static):这个内存区域主要存放静态变量、全局变量,生命周期也是全局,只有程序运行结束系统才会回收其空间。
4.代码段:这个区域内存主要存放函数体(类成员函数和全局函数)的二级制代码。

有了上面那幅图,相信你对动态内存管理又有了更深的理解,也可以更好的理解static这个关键字了:
1.一般的临时变量开在栈区,栈区的特点是,出了作用域,里面的临时变量就会被销毁。
2.static修饰的静态变量在数据段,生命周期变长了,在程序运行结束的时候它的空间才会被系统回收。

3.柔性数组

3.1什么是柔性数组

C99标准中,如果结构体的最后一个数组它的大小是未知的,我们就把那个数组叫做柔性数组。

typedef struct ss
{
	int a;
	int b[0];
}flexarr;

如果上面那种你的编译器不能通过,可以尝试写成下面这样:

typedef  struct sss
{
	int a;
	int b[];
}flexarr;

关于柔性数组有几点需要说明的:
1.sizeof计算结构体的大小时,是不将柔性数组的大小计算在内的。
2.当你要给柔性数组用malloc()函数开空间时,大小应该要比结构体的大小要大,以便于系统给柔性数组分配空间。
3.柔性数组前面必须要有至少要有一个成员。
如果你不相信我们可以用下面代码来验证一下:

#include
#include
#include
struct ss
{
	int a;
	int b[0];
}flexarr;

int main()
{
	printf("%d", sizeof(flexarr));
	return 0;
}

运行结果:
【C语言进阶】之动态内存管理笔试题及柔性数组_第8张图片

3.2柔性数组的使用

通过下面代码我们来演示柔性数组的使用:

#include
#include
#include
typedef struct ss
{
	int a;
	int b[0];
}flexarr;

int main()
{
	flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	p->a = 100;
	memset(p->b, 0, sizeof(int) * 100);
	for (int i = 0; i < 100; i++)
	{
		p->b[i] += 1;
	}
	printf("%d\n", p->a);
	for (int i = 0; i < 100; i++)
	{
		printf("%d ", p->b[i]);
	}
	free(p);
	return 0;
}

运行结果:

【C语言进阶】之动态内存管理笔试题及柔性数组_第9张图片

flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100 * sizeof(int));

这个代码相当于给柔性数组开了100个int的空间。

3.2柔性数组的优点

上述flexarr也可以这样设计:


#include
#include
#include
typedef struct ss
{
	int a;
	int* b;
}flexarr;

int main()
{
	flexarr* p = (flexarr*)malloc(sizeof(flexarr) + 100);
	if (p == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	p->a = 100;
	p->b = (int*)malloc(sizeof(int) * 100);
	memset(p->b, 0, sizeof(int) * 100);
	for (int i = 0; i < 100; i++)
	{
		p->b[i] += 1;
	}
	printf("%d\n", p->a);
	for (int i = 0; i < 100; i++)
	{
		printf("%d ", p->b[i]);
	}
	free(p->b);
	p->b = NULL;
	free(p);
	p = NULL;
	return 0;
}

运行结果:

【C语言进阶】之动态内存管理笔试题及柔性数组_第10张图片

第一种方法和第二种相似,但是柔性数组有一定的优势:
1.方便内存释放

  • 如果我们的代码是在一个函数里面,给用户使用,用户是看不到我们具体的实现,给用户返回一个结构体,你在里面进行二次内存分配,用户只知道释放结构体的大小,怎么能知道还需要释放里面的动态数组呢?这样就会造成内存泄漏,不安全。
  • 2.提高了访问速度
    代码1使用柔性数组,只进行了一次内存分配,是连续的,可以提高访问速度,减少了内存碎片。

你可能感兴趣的:(C语言进阶篇,c语言,柔性数组,开发语言,c++,java,算法)