目录
思维导图:
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);
}
}
这样就不会出错了。
写在最后:
以上就是本篇文章的内容了,感谢你的阅读。
如果喜欢本文的话,欢迎点赞和评论,写下你的见解。
如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。
之后我还会输出更多高质量内容,欢迎收看。