//1.
p = (int*)realloc(p, 20 * sizeof(int));
//2.
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr == NULL) {
return -1;
}
p = ptr;
>
> 防止增容失败将原空间指针置空,故不可直接使用原指针接受返回值。判断非空后再赋给原指针。
>
>
>
#### 常见的动态内存错误
##### 1.不检查空指针
void test() {
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;
free§;
}
对指向动态开辟的空间的指针一定要做有效的判断。
##### 2.越界访问
void test() {
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
if (NULL == p) {
exit(EXIT_FAILURE);
}
for (int i = 0; i <= 10; i++) {
*(p + i) = i;
}
free§;
p = NULL;
}
作为程序员必须有意识地检查所写的代码是否有越界访问的问题。
##### 3.释放非动态开辟内存
void test() {
int a = 10;
int* p = &a;
free§;
p = NULL;
}
不可用`free`释放非动态开辟的空间。
##### 4.释放部分内存
int main()
{
int* p = (int*)malloc(100);
p++;
free§;
return 0;
}
改变指向动态开辟内存的指针,内存将无法管理。释放不完全导致内存泄漏。
##### 5.重复释放内存
void test() {
int* p = (int*)malloc(100);
free§;
free§;
}
使用`free`释放已释放的空间,即访问非法内存。建议释放内存和指针置空搭配使用。
##### 6.忘记释放内存
void test() {
int *p = (int*)malloc(100);
if(NULL != p) {
*p = 20;
}
}
int main() {
test();
while(1);
}
使用结束不释放内存造成内存泄漏。程序不停止,系统也不会自动回收。
#### 笔试题
>
> 调用下列`test`函数,解释运行结果。
>
>
>
##### Example 1
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;
}
>
> 程序报错。
>
>
>
**传值调用**:并没有改变`str`的值仍为不予修改的空指针,可以使用二级指针接收`str`的地址。函数调用结束后指针销毁故无法释放空间以致内存泄漏。
##### Example 2
char* GetMemory() {
char p[] = “hello world”;
return p;
}
void test() {
char* str = NULL;
str = GetMemory();
printf(str);
free(str);
str = NULL;
}
>
> 程序打印随机值。
>
>
>
**返回栈空间地址**:数组`p`在函数内创建,出函数销毁,返回这部分空间的地址 ,属于访问非法空间。
##### Example 3
void GetMemory(char** p,int num) {
*p = (char*)malloc(num);
}
void test() {
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, “hello”);
printf(str);
free(str);
str = NULL;
}
>
> 程序运行成功,打印`"hello"`。
>
>
>
**传址调用**:本题是例一的正确写法。
##### Example 4
void test(void) {
char* str = (char*)malloc(100);
strcpy(str, “hello”);
free(str);
if (str != NULL) {
strcpy(str, “world”);
printf(str);
}
}
>
> 程序报错。
>
>
>
**野指针**:动态开辟的内存释放后指针不置空,造成野指针访问非法内存。释放内存和指针置空应该搭配起来使用。
>
> 释放空间,销毁空间都是将内存空间归还给操作系统,即将此空间的使用权限归还操作系统。虽不会改变空间内容以致打印出所谓的“正确结果”,但可能在之后被操作系统分配给其他程序时发生修改。但无论改变与否,一旦空间归还后再去访问就是访问非法内存。
>
>
>
#### C/C++内存划分
##### 用例展示
>
> 根据下列创建的各种变量,分析内存的划分。
>
>
>
int globalVar = 1;
static int staticGlobalVar = 1;
int main()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1,2,3,4 };
char char2[] = "abcd";
char\* pChar3 = "abcd";
int\* ptr1 = (int\*)malloc(4 \* sizeof(int));
int\* ptr2 = (int\*)calloc(4, sizeof(int));
int\* ptr3 = (int\*)realloc(ptr2, 4 \* sizeof(int));
free(ptr1);
free(ptr3);
return 0;
}
1. `globalVal`,`staticGobalVar`,`staticVar`分别是全局变量和静态变量,在数据段上创建。
2. `localVar`和`num`,`char2`,`pchar`以及`ptr`本身都是局部变量,都是在栈区上创建的。
3. `malloc`,`calloc`,`realloc`都是在堆区上开辟的内存块,由指针`ptr`指向而已。
##### 内存划分图示
![](https://img-blog.csdnimg.cn/img_convert/b05533a063fac9b6d6a21e748b31aeb6.png)
1. 栈区(`stack`):执行函数时,函数的局部变量都会在栈区上创建。压栈:从栈顶向下开辟空间,弹栈:从栈底向上释放空间。
2. 堆区(`heap`):一般由程序员分配和释放,从堆低向上开辟空间,堆顶向下释放空间。在程序结束后也被操作系统会自动回收。
3. 数据段(静态区):存放全局变量,静态数据。变量本在栈上创建,被`static`修饰后放在常量区,程序结束后由系统释放。
4. 代码段(常量区):存放可执行代码和只读常量。
>
> 语言学习时期,仅对内存作此了解即可。内核空间和内存映射段会在操作系统中学习,此处不再深入研究。
>
>
>
#### 柔性数组
>
> C99中引入柔性数组。**柔性数组**(flexible array)面试中虽不是重要的考点,但仍需掌握最基本的使用。
>
>
>
##### 柔性数组的定义
在`C99`中,结构中最后一个元素允许是未知大小的数组,被称为柔性数组成员。例如:
//1.
struct st_type {
int i;
int a[0];//柔性数组成员
};
//2.
struct st_type {
int i;
int a[];//柔性数组成员
};
语法规定数组大小中写0和不写都代表不指定大小。意味数组可大可小,这便是柔性的含义。
>
> 有些编译器可能只支持一种写法。当然柔性数组前必须有其他成员,类型一致是为了避免考虑内存对齐。既然把柔性数组放在动态内存管理一章,可见二者有必然的联系。
>
>
>
##### 柔性数组的特点
* 结构中柔性数组成员前必须至少有一个成员。
* `sizeof`计算结构所占空间时不包含柔性数组的大小。
* 包含柔性数组的结构用`malloc`进行动态内存分配,且分配的内存应大于结构大小,以满足柔性数组的预期。
>
> 使用含柔性数组的结构体,需配合以`malloc`等动态内存分配函数。分配空间减去其他成员的大小,即为为柔性数组开辟的空间。
>
>
>
![](https://img-blog.csdnimg.cn/img_convert/8f65832228fd2446e043539651dee276.png)
##### 柔性数组的使用
![](https://img-blog.csdnimg.cn/img_convert/d9590fd6cfb7710ecdb05a50c16c7a49.png)
>
> `malloc`开辟的大小写成如图所示的形式,增加代码的可阅读性。
>
>
> 结构体所分配空间减去其他成员的大小,所剩即为为柔性数组开辟的空间大小,若不够还可以用`realloc`调整大小,以满足柔性数组“柔性”的需求。
>
>
>
struct st_type {
int i;
int a[0];
};
int main() {
printf(“%d\n”, sizeof(struct st_type));
//1.
struct st_type st;
//2.
struct st_type* pst = (struct st_type*)malloc(sizeof(struct st_type) + 10 * sizeof(int));
if (pst == NULL) {
perror(“pst”);
return -1;
}
return 0;
}
含柔性数组结构体当然不可像第一种那样使用,这样结构体变量`st`仅有4个字节,不包含柔性数组。
###### Example
struct st_type {
int i;
int a[0];
};
int main() {
struct st_type* pst = (struct st_type*)malloc(sizeof(struct st_type) + 10 * sizeof(int));
if (pst == NULL) {
perror(“pst”);
return -1;
}
pst->i = 10;
for (int i = 0; i < 10; i++) {
printf("%d “, pst->a[i] = i);
}
//调整空间大小
struct st_type* ptr = (struct st_type*)realloc(pst, sizeof(struct st_type) + 20 * sizeof(int));
if (ptr == NULL) {
perror(“ptr”);
return -1;
}
pst = ptr;
for (int i = 10; i < 20; i++) {
printf(”%d ", pst->a[i] = i);
}
//释放
free(pst);
pst = NULL;
return 0;
}
##### 柔性数组的优势
>
> 柔性数组成员利用动态内存可大可小,那同样将柔性数组成员替换成指向动态开辟内存的指针也可达到同样的效果。下文将对比二者都有何优劣。(为突出对比,已省略不必要的代码)
>
>
>
###### 柔性数组版本
还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!
王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。
对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!
【完整版领取方式在文末!!】
***93道网络安全面试题***
![](https://img-blog.csdnimg.cn/img_convert/6679c89ccd849f9504c48bb02882ef8d.png)
![](https://img-blog.csdnimg.cn/img_convert/07ce1a919614bde78921fb2f8ddf0c2f.png)
![](https://img-blog.csdnimg.cn/img_convert/44238619c3ba2d672b5b8dc4a529b01d.png)
内容实在太多,不一一截图了
### 黑客学习资源推荐
最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
#### 1️⃣零基础入门
##### ① 学习路线
对于从来没有接触过网络安全的同学,我们帮你准备了详细的**学习成长路线图**。可以说是**最科学最系统的学习路线**,大家跟着这个大的方向学习准没问题。
![image](https://img-blog.csdnimg.cn/img_convert/acb3c4714e29498573a58a3c79c775da.gif#pic_center)
##### ② 路线对应学习视频
同时每个成长路线对应的板块都有配套的视频提供:
![image-20231025112050764](https://img-blog.csdnimg.cn/874ad4fd3dbe4f6bb3bff17885655014.png#pic_center)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**