C语言-柔性数组与几道动态内存相关的经典笔试题(12.2)

目录

思维导图:

1.柔性数组

1.1柔性数组的特点

1.2柔性数组的使用

1.3柔性数组的优势

2.几道经典笔试题

2.1题目1

2.2题目2

2.3题目3

2.4题目4

写在最后:


思维导图:

1.柔性数组

1.1柔性数组的特点

例:

#include <stdio.h>

typedef struct S
{
	int n;
	char arr[];//大小是未知的//这是柔性数组成员
}S;

int main()
{
	printf("&d\n", sizeof(S));//不计算大小
}

输出:

输出:4

柔性数组的特点:

1.结构中的柔性数组成员前面必须至少一个其他成员。

2.sizeof 返回的这种结构大小不包括柔性数组的内存。

3.包含柔性数组成员的结构用malloc函数进行内存的动态分配,

并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

1.2柔性数组的使用

例:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct S
{
	int n;
	char arr[];//大小是未知的
}S;

int main()
{
	//开辟
	S* ps = (S*)malloc(sizeof(S) + 10 * sizeof(char));//结构体大小+柔性数组需要的大小

	//判断
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}

	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = 'Q';
		printf("%c ", ps->arr[i]);
	}
	//增容
	//.....

	//释放
	free(ps);
	ps = NULL;
	return 0;
}

输出:

输出:Q Q Q Q Q Q Q Q Q Q

1.3柔性数组的优势

其实柔性数组能实现的操作,利用指针也能实现:

例:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct S
{
	int n;
	char* arr;
}S;

int main()
{
	//开辟内存空间
	S* ps = (S*)malloc(sizeof(S));
	if (ps == NULL)
	{
		return 1;
	}
	ps->n = 100;
	ps->arr = (char*)malloc(sizeof(char) * 10);
	if (ps->arr == NULL)
	{
		perror("malloc:");
		return 1;
	}

	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = 'Q';
		printf("%c ", ps->arr[i]);
	}

	//增容
	char*ptr=(char*)realloc(ps->arr, 20 * sizeof(char));
	if (ptr != NULL)
	{
		ps->arr = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}

	//释放
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

输出:

输出:Q Q Q Q Q Q Q Q Q Q

但是用柔性数组实现更好一些:

1.使用柔性数组方便内存的释放,只需要一次free。

2.连续的内存有益于提高访问速度,也有益于减少内存碎片。

总而言之,柔性数组给我们解决问题提供了更多的可能。

2.几道经典笔试题

2.1题目1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void GetMemory(char* p)
{
	p = (char*)malloc(100);//申请内存地址
}//p变量销毁了,malloc空间未释放,导致内存泄漏

void test()
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");//访问的是0地址//不允许访问//程序崩溃
	printf(str);
}

注:记得及时释放开辟的动态内存。

正确的写法:

我们可以通过传值调用使传过去的指针指向开辟动态内存。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//正确的写法
void GetMemory(char** p)
{
	*p = (char*)malloc(100);//申请内存地址
}

void test()
{
	char* str = NULL;
	GetMemory(&str);//传址调用
	strcpy(str, "hello world");
	printf(str);
	//释放
	free(str);
	str = NULL;
}

int main()
{
	test();
	return 0;
}

输出:

输出:hello world

当然,也可以通过函数返回值的形式,

将指向动态内存空间的指针返回:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//更多的方法
char* GetMemory()
{
	char*p = (char*)malloc(100);//申请内存地址
	return p;//返回地址
}

void test()
{
	char* str = NULL;
	str = GetMemory();
	strcpy(str, "hello world");
	printf(str);
	//释放
	free(str);
	str = NULL;
}

int main()
{
	test();
	return 0;
}

输出:

输出:hello world

2.2题目2

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char* GetMemory()
{
	char p[] = "hello world";
	return p;//返回栈空间的地址
}//p数组销毁了

void test()
{
	char* str = NULL;
	str = GetMemory();
	printf(str);//非法访问	
}

int main()
{
	test();
	return 0;
}

注 :不要访问没有开辟的内存空间,非常危险。

再看一个类似的例子:

#include <stdio.h>

//类似的问题
int* test()
{
	int a = 0;
	return &a;
}//int a的空间被销毁了

int main()
{
	int* p = test();
	printf("hehe\n");//栈区中原本存放a变量的空间被覆盖了
	printf("%d\n", *p);//打印随机值(非法访问)
	return 0;
}

输出:

输出:5

最后就输出了个随机值。

2.3题目3

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);//开辟空间
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);//传址调用

	//使用
	strcpy(str, "hello");
	printf(str);

	//我们发现它没有释放内存//导致内存泄漏
}

注:一定要记得释放内存!

2.4题目4

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void test()
{
	char* str = (char*)malloc(100);//开辟空间
	strcpy(str, "hello");
	free(str);//str释放了
	if (str != NULL)//str被释放后已经是野指针了
	{
		strcpy(str, "world");//非法访问
		printf(str);
	}
}

int main()
{
	test();
	return 0;
}

指针释放后再使用会导致野指针。

我们可以改进这段代码:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void test()
{
	char* str = (char*)malloc(100);//开辟空间
	strcpy(str, "hello");
	free(str);//str释放了
	str = NULL;//主动置为空才行
	if (str != NULL)
	{
		strcpy(str, "world");//非法访问
		printf(str);
	}
}

这样就不会出错了。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看。

猜你喜欢

转载自blog.csdn.net/Locky136/article/details/128685853