思维导图:
1.柔性数组
1.1柔性数组的特点
例:
#include typedef struct S { int n; char arr[];//大小是未知的//这是柔性数组成员 }S; int main() { printf("&d\n", sizeof(S));//不计算大小 }
输出:
输出:4
柔性数组的特点:
1.结构中的柔性数组成员前面必须至少一个其他成员。
2.sizeof 返回的这种结构大小不包括柔性数组的内存。
3.包含柔性数组成员的结构用malloc函数进行内存的动态分配,
并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
1.2柔性数组的使用
例:
#include #include #include 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 #include #include 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 #include #include 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 #include #include //正确的写法 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 #include #include //更多的方法 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 #include #include char* GetMemory() { char p[] = "hello world"; return p;//返回栈空间的地址 }//p数组销毁了 void test() { char* str = NULL; str = GetMemory(); printf(str);//非法访问 } int main() { test(); return 0; }
注 :不要访问没有开辟的内存空间,非常危险。
再看一个类似的例子:
#include //类似的问题 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 #include #include 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 #include #include 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 #include #include void test() { char* str = (char*)malloc(100);//开辟空间 strcpy(str, "hello"); free(str);//str释放了 str = NULL;//主动置为空才行 if (str != NULL) { strcpy(str, "world");//非法访问 printf(str); } }
这样就不会出错了。
写在最后:
以上就是本篇文章的内容了,感谢你的阅读。
如果喜欢本文的话,欢迎点赞和评论,写下你的见解。
如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。
之后我还会输出更多高质量内容,欢迎收看。