#include
struct s // 班级成员信息
{
char name[20];
int age;
};
int main()
{
struct s arr[50];// 50 个 struct s 类型的数据
// 但是如果班级没50人,那么就意味着要浪费空间
// 反之班级人数超过50个,空间就不够用
// 如果 在前面加上 int n =0; scanf("%d",&n); n 取代 50 的位数行不行呢?
// 答案不行 因为 n 是一个变量, 而数组的元素个数应该给一个常量
return 0;
}
程序二:
#include
#include // malloc 所在头文件
#include // errno,h 错误码库
int main() // 与 函数 strerror(错误信息报告) 搭配使用,将错误码转化为错误信息,并返回它的地址
{
// 我想向内存申请 10个 整形的 空间
int* p = (int*)malloc(/*INT_MAX*/10 * (sizeof(int)));// 因为 malloc 函数 返回的是 万能*(无类型)指针,所以需要转化类型
// malloc 函数 也有 开辟空间失败的时候,
// 比如 内存 只有 4 个 g ,我要开辟 8 g 空间,这肯定是会失败的,返回 空指针 NULL
if (p == NULL)
{
// 找出打印错误原因的方式
printf("%s\n", strerror(errno));// malloc(INT_MAX)
//输出 Not enough space
}
else
{
// 正常使用 空间
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;// 赋值 0 1 2 3 4 5 6 7 8 9
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));// 输出 0 1 2 3 4 5 6 7 8 9
}
printf("\n");
}
// 当动态申请的空间 不再使用的时候
// 就会把空间 还给 操作系统
// 这时候 就会用到 free 函数
return 0;
}
程序三:
#include
#include // malloc,free 所在头文件
#include
int main()
{
// 我想向内存申请 10个 整形的 空间
int* p = (int*)malloc(10 * (sizeof(int)));
if (p == NULL)
{
// 找出打印错误原因的方式
printf("%s\n", strerror(errno));// malloc(INT_MAX)
//输出 Not enough space
}
else
{
// 正常使用 空间
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;// 0 1 2 3 4 5 6 7 8 9
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));// 0 1 2 3 4 5 6 7 8 9
}
printf("\n");
}
// 当动态申请的空间 不再使用的时候
// 就会把空间 还给 操作系统
// 这时候 就会用到 free 函数
free(p);// 用完了再释放,也就是说 在打印完之后(调用完之后),再释放(p的值没有改变,还可以通过它找到该空间)
// 虽然当前空间 不属于当前程序,但是依然 可以通过 p 找到 该空间,很有可能会破坏这个空间
// 所以先在这个 指针 依然很危险
p = NULL; // free函数 释放完空间后,不会改 p 的值,所以我们自己主动改
return 0;
}
// 最后请注意:free 是用来 释放 动态开辟的内存
// free 函数 的 特殊情况:
//1. 如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的
//2. 如果参数 ptr 是 NULL 指针,则函数 什么 都不做。
程序四:
用法跟 malloc 函数差不多
#include
#include
#include
int main()
{
// 开一块空间,空间大小为 10 * int == 40 字节
int* p = (int*)calloc(10, sizeof(int));// 比 malloc 函数 多一个元素个数 参数
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
else
{
int i = 0;
for (i = 0; i < 10; i++)// 因为 p 为整形指针变量,指向一个 int 类型 元素,所以有 10个 int 元素
{
printf("%d ", *(p + i));// 0 0 0 0 0 0 0 0 0 0
} //calloc 在开辟好空间后,会把空间的所有字节内容全部初始化为 零
}
free(p);
p = NULL;// 狗头拿来!!
return 0;
}
#include
#include
#include
int main()
{
int* p = (int*)malloc(20);// 开辟 20 byte 动态空间
if (p == NULL) // 防止malloc 开辟动态空间失败
{
printf("%s\n", strerror(errno));// 打印错误信息
}
else
{
// 正常使用空间
int i = 0;// 20 字节 == 5* (sizeof(int))
for (i = 0; i < 5; i++)
{
*(p + i) = i;// 此时 就是在使用 malloc 函数开辟的空间(20byte)
}
}
// 假设 malloc 函数 开辟的 20 byte 的空间,不满足我们的需求(不够)
// 我们希望能够有 40 个 byte 的空间
//这里 就可以使用 realloc 函数 来调整动态开辟的内存
int* p2 = (int*)realloc(p, 40);// 10 * int == 40 byte
/* p */
// 记住 只要开辟动态空间的时候,最好判断一下,无论是 malloc ,calloc还是realloc 函数 都有可能开辟空间失败
// 在下面的解决方案中,已得到解决
// 你会发现 原本 整个空间是 由 p 来维护的
// 但是 现在你会发现 变成了 p2 来维护 了
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p2/* p */ + i));// 0 1 2 3 4 随机值1 随机值2 随机值3 随机值4 随机值5
} // 随机值 是因为 malloc 不会像 calloc 函数 一样开辟完动态空间之后,初始化为 0
printf("\n");
for (i = 5; i < 10; i++) // 使用 增大后的空间
{
*(p2/* p */ + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p2/* p */ + i));// 0 1 2 3 4 5 6 7 8 9
} // 如果保持统一 ,p2 换成 p,会出现问题(p已将找不到了)
// 因为 realloc 函数,是重新创建一个(40 byte)空间
// 把 p 的内容拷贝下来,把拷贝下来的数据 放进 这新创建的空间里
// 然后把 p 释放(p = NULL)
// 最后 返回新空间的地址
// 输出这个新创建的空间 的结果 让你 感觉 空间确实变大了
// 实际上 已经 不是 原先的空间 了
return 0;
}
程序六:
#include
#include
#include
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
else
{
int i = 0;// 20 字节 == 5* (sizeof(int))
for (i = 0; i < 5; i++)
{
*(p + i) = i;// 此时 就是在使用 malloc 函数开辟的空间(20byte)
}
}
int* ptr = (int*)realloc(p, 40);// 拿一个新的指针变量 ptr 先来接收 realloc 的返回地址
if (ptr != NULL) // 判断 realloc 开辟动态空间是否 成功
{
p = ptr;// 防止开辟失败,把 p 改掉了
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));// 0 1 2 3 4 随机值1 随机值2 随机值3 随机值4 随机值5
}
printf("\n");
for (i = 5; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));// 0 1 2 3 4 5 6 7 8 9
}
}
// 释放内存
free(p);// 如果 realloc 开辟动态空间失败,返回 NULL,而 free(NULL),free 函数什么都不会做
p = NULL;
// 一定 要释放内存,并置为空指针,永绝后患!
return 0;
}
程序七:
#include
int main()
{
int* p = (int*)realloc(NULL, 40);// 此时它的功能 与 malloc 函数一样
// == (int*)malloc(40);
}
程序八:
#include
#include
int main()
{
int* p = (int*)malloc(40);
// 万一 malloc 失败,p 被赋值为 NULL
for (int i = 0; i < 10; i++)
{
*(p + i) = i;// 非法地址,对 NULL 指针 进行解引用
}
free(p);
p = NULL;
return 0;
//解决方案 :在使用开辟的动态空间之前,用 assert(p),记得加上头文件 assert.h; 或 加上 if(p != NULL) 判断语句,对 p 进行相关的判断
}
程序九:
#include
#include
int main()
{
int* p = (int*)malloc(20);// 开辟了 5个 int 元素的空间
if (p == NULL)// 判断 malloc 函数 开辟动态空间 是否成功
{
return 0; // 失败
}
else// 成功
{
int i = 0;
for (i = 0; i < 10; i++) // 我只有 5个 int元素,而现在 要访问 第 6~10 个 int 元素,形成了越界访问,导致程序崩溃
{
// 改成 5 就没问题了
*(p + i) = i;
}
}
free(p);
p = NULL;
}
程序十:
#include
int main()
{
int a = 10;// 在 栈上 开辟空间
int* p = &a;
*p = 20;
free(p); // free 对 非动态 开辟内存 使用,会造成程序崩溃
p = NULL;
return 0;
}
程序十一:
#include
#include
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 0;
}
else
{
int i = 0;
for (i = 0; i < 10; i++)
{
*p++ = i;// 解决方法 *(p+i)= i; 这样避免了 p 的地址被改变
}
// 回收空间
free(p);// 如果这样直接这样释放空间,会导致程序崩溃
// 因为 p 已经不再指向该 动态开辟空间 的 起始位置,它释放第 10 个元素之后的空间(释放一部分动态开辟的空间)
// free 只能释放 动态开辟空间 的起始地址(要释放就全释放)
}
p = NULL;
return 0;
}
程序十二:
#include
#include
int main()
{
int* p = (int*)malloc(40);
if (p = NULL)
{
return 0;
}
// 使用
// 释放
free(p);
free(p);// free 释放 一块动态开辟空间后,那个 空间 的地址并没有改变,此时再 free 就可能出现问题
return 0;
} // 谁申请 谁回收
// 在 free(); 后面 手动 把 p = NULL; 后面就算再接上一个 free,也不会有问题
// 因为 根据C语言标准:free(NULL); free 什么都不做
程序十三:
#include
#include
int main()
{
while (1)
{
malloc(1);
// 通过 三点(Ctrl、Alt 点 .)一线(同时按),选择任务管理器 -》 性能
// 通过观察 内存 占用 显示发现,内存正在被大量消耗,
// 原因是 程序一直在申请空间,不回收,导致大量内存空间被占用(这些你不用,别人也用不了,因为你没还),这就是 内存泄露
}
}
程序十四:
#include
#include
#include
void GetMemory(char* p)// p 是 str 的一份临时拷贝,即 p= NULL;
{
// malloc 在堆上开辟了一块空间,把该动态开辟空间的起始地址赋给 p
p = (char*)malloc(100);// 此时 p 指向 malloc 开辟的动态空间
// 动态开辟空间,调用完了,没有释放空间,会造成 内存泄露
// 而且 这函数调用完后之后,这块函数空间会被回收
// 到时我们再也找不到 这动态开辟的空间的起始地址
// 也就是说 出了这个函数 想解决 内存泄露 这个问题,都解决不了(p 是该函数的形参,只在该函数内部有效)
再举个例子,
假设有一个卧底(malloc开辟的动态空间),他的信息(空间的地址)在警察系统中都删掉了,只有 p 知道,
后来,有一天 p 死掉了(出了函数,销毁了 局部变量 p).他也没有记本本告诉别人(没有返回这块空间的地址),
那么这个卧底再也证明不了自己的身份(丢失了开辟动态空间的地址),就像无间道里一样。
没有人可以证明 他 是一个警察了(没有人知道 这块的空间的地址)
}
void test(void)
{
char* str = NULL;// dtr 的 空间 放了一个 空指针
GetMemory(str);// 传值:传的是 指针变量 str 本身(内容),不是地址
strcpy(str, "hello world");// str 还是 空指针,因为 GetMemory(str) 没有 返回值,也没有通过地址 改变str的值
// 该表达式 意思是把 "hello world" 这个字符串 赋给 str 这个空指针 所指向的空间(无效的空间)
// "hello world" 是不能赋过去的,这样会导致程序崩溃,因为访问内存已经失败了
// str并没有 指向 有效空间里,你非要把 "hello world" ,那我们就 解引用空指针,对空指针进行相印的遍历
// 空指针的值 == 0, 0 作为地址,找一个空间。在 strcpy 肯定是要 str++ ,往后面找空间。(0 作为地址,对指向的空间赋值,然后++,再进行赋值,以此类推)
// 这样做 必然 会 造成 非法访问内存,从而导致 程序崩溃。
// 因为程序已经崩溃了,也就更谈不上后面的打印,虽然 打印的格式没有错误,但已经打印不出东西了
printf(str); // 通过下面一个程序,可以说明该 打印方式 没有问题
// == printf("hello world");
}
int main()
{
test();
return 0;
}
程序十五:
#include
int main()
{
char* str = "abcdef";
printf("%s\n", str);// abcdef
printf(str);// abcdef
return 0;
}
程序十六:
#include
#include
#include
void GetMemory(char** p)// str 是一个一级指针,要二级指针来接收它地址
{
// *p == str (通过地址 找到了 str)
*p = (char*)malloc(100);// 把 malloc 开辟的动态空间的起始地址赋给 *p,也就是 赋给 str
// 也就说 p 在死之前,把 卧底的身份写在小本本上, 送给 str 这个人,所以 str 知道卧底的真实身份
}
void test(void)
{
char* str = NULL;
//传址
GetMemory( &str );// GetMemory - 获取一块内存块;
//把这块内存 存入 str中,方便下方字符串 拷贝
strcpy(str, "hello world");// 此时 str 不再是 NULL, 因为 GetMemory 赋给 它 一个 有效的 动态空间 的地址
// strcpy 可以 吧 "hello world" 赋给 str 指向的空间(malloc 函数 开辟的动态空间)
printf(str); // hello world
// str 跟这个卧底 对了一波 暗号 ==
//但是别忘了 释放动态开辟的空间,我 们通过的 指针的方式(*p == str), 把地址带回来了
free(str);
str = NULL; // 并 向卧底保证,目前并不会揭穿他的身份,因为卧底不想暴露身份(其实我怀疑他上瘾了。)
}
int main()
{
test();
return 0;
}
程序十七:
#include
#include
#include
char* GetMemory(char* p)// str存的值是一个地址,要一个指针来接收它地址
{
p = (char*)malloc(100); //可以这么理解, 卧底(malloc 开辟的动态空间),通过 p 死之前,留下的信息,去找 str 这个知情人。
return p;
}
void test(void)
{
char* str = NULL;
str = GetMemory(str);//传值
// 卧底找到了 str 这个知情人
strcpy(str, "hello world");// 保险起见,对了波暗号
printf(str); // hello world
free(str); // str 确认了其身份,并保证不泄露他的身份,成为他证人
str = NULL;// 不曾想,卧底趁str不注意,宰了 str 这个人。(看来他是真的上瘾了)
}
int main()
{
test();
return 0;
}
程序十八:
#include
char* GetMemory(void)
{
char p[] = "hello world";// 局部数组( 栈上 开辟 !!!!),出了函数 就会被销毁(还给操作系统)
return p;// 这里返回的是一个野指针
}
void test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);// 访问野指针,属于非法访问,而且再次访问 不一定就是 hello world,很可能是乱码
}
int main()
{
test();
return 0;
}
程序十九:
#include
#include
#include
void GetMemory(char* *p, int num)
{
*p = (char*)malloc(num); // 通过地址 改变了 str 的值
}
void test(void)
{
char* str = NULL;
GetMemory(&str, 100);
// if(str != NULL) 或者 assert(str),判断 malloc 开辟动态空间 是否成功,
// 万一开闭失败,str 为 NULL, 导致拷贝失败,程序崩溃,这也是一个隐藏问题
strcpy(str, "hello world");
printf(str);// 打印 hello world ,但存在内存泄露,未释放动态开辟的空间
// free(str);
// str = NULL;
}
int main()
{
test();
return 0;
}
程序二十:
#include
#include
#include
void test(void)
{
char* str = (char*)malloc(100);
if (str)// 判断 malloc 函数 是否开辟空间成功
{
strcpy(str, "hello");
free(str);// 释放了内存,但没有 将其 置为 NULL
//加上 str = NULL; 没有问题
if (str != NULL)// 条件为真
{
strcpy(str, "world");// 非法访问 内存,str 所指向的空间已不属于它
printf(str);// 可能输出 world ,也可以是乱码,因为这块空间已经被释放,你再对它进行访问操作
// 什么情况都可能出现
}
}
}
int main()
{
test();
return 0;
}
#include
struct s
{
int n;
// 第一种写法
int arr[];// 最后一个成员,可以是未知大小的
// 第二种写法
// int arr2[0];
// 以上 这种数组 的 大小是未知的 ,被称为 柔性数组 成员
// 柔性 : 这个 数组 的 大小 是 可调整的
};
int main()
{
return 0;
}
#include
#include
struct s // 该结构体大小为 4
{
int n;
int arr[];
};
int main()
{
//struct s s; //sizeof 返回的这种结构大小不包括柔型数组的内存
//printf("%d\n", sizeof(s));// 4, 也就说没有包含 这个 柔性数组 的大小
struct s* ps = (struct s*)malloc(sizeof(struct s) + 5 * sizeof(int));
// 开辟 24 byte 的空间
// 口口口口 口口口口口口口口口口口口口口口口口口口口
// n arr
// 然后把这块空间的地址赋给 结构体指针变量 ps
// 对于 ps 来说 这块空间是 一个结构体的空间,前四个字节 是 成员 n 的空间, 后 20 个字节 是 成员 arr 的空间
int i = 0;
if (ps)// 判断 malloc 开辟动态空间是否成功 如果 ps = NULL == 0,因为 NULL 的值 为 0,条件为假跳过if语句
{
ps->n = 100;
for (i = 0; i < 5; i++)
{
ps->arr[i] = i;// 0 1 2 3 4
printf("%d ", ps->arr[i]); // 0 1 2 3 4
}
printf("\n");
// 假设 前面 的 5 个int, 不够怎么办,要10个int , 这时就要用 realloc 函数 来扩展
struct s* ptr = (struct s *)realloc(ps, 44);// 4 byte 结构体大小 + 柔性数组的大小 40 byte
if (ptr)// 判断 realloc 创建 动态 是否成功
{
ps = ptr;
for (i = 5; i < 10; i++)
{
ps->arr[i] = i;// 5 6 7 8 9
}
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]); // 0 1 2 3 4 5 6 7 8 9
}
free(ps);// 用完 动态空间,一定要记得释放
ps = NULL;
}
}
return 0;
}
#include
#include
struct s
{
int n;
int* arr;// arr 整形指针
};
int main()
{
struct s* ps = (struct s*) malloc(sizeof(struct s));// n 和 指针变量 arr 的 空间
int i = 0;
if (ps)
{
ps->arr = malloc(20);// 开辟一块 20 byte 的动态空间,将其地址 赋给 指针变量 arr
if (ps->arr)
{
for (i = 0; i < 5; i++)// 赋值
{
ps->arr[i] = i;
printf("%d ", ps->arr[i]);// 0 1 2 3 4
} // 图方便 少写一个 打印 for 循环
printf("\n");
}
//调整大小
int* ptr = (int*)realloc(ps->arr,40);// 开辟一块 40 byte 的动态空间,将其地址 赋予 指针变量 arr
if (ptr) // 判断 realloc 开辟动态空间是否成功
{
ps ->arr = ptr;
for (i = 5; i < 10; i++)
{
ps->arr[i] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]);// 0 1 2 3 4 5 6 7 8 9
}
}
}
free(ps->arr);// 要先释放arr,如果先释放 ps 会找不到指针变量 arr
free(ps);
ps->arr = NULL;
ps = NULL;
return 0;
}