一 内存区域划分
1 代码区
存代码
2 常量区
存常量:数值常量 字符常量 字符串常量 符号常量(define) 地址常量…
3 全局区(静态全局区)
关键词:
- 静态变量(static) static:静态
- 全局变量
#include<stdio.h>
int b = 0; //普通全局变量
static int d = 0; //静态全局变量
int main() {
int a = 0; //普通局部变量(主函数范围内)
static int c = 0; //静态局部变量
return 0;
}
作用与区别:
- 作用域 : 起作用的区域
- 生存周期(生命周期) : 起作用的时间段
#include<stdio.h>
//全局变量生存周期:程序开始到程序结束
int b = 0; //普通全局变量
//普通全局变量作用域(项目作用域):作用域为整个项目,不同文件时用extern声明
//extern: extern + 完整定义
//
static int d = 0; //静态全局变量
//静态全局变量作用域(文件作用域):作用域为当前文件
int main() {
//作用域: 当前语块(变量定义到大括号结束)
int a = 0; //普通局部变量
//生存周期:当前语块
static int c = 0; //静态局部变量
//生存周期:程序开始到程序结束
return 0;
}
void fun() {
a = 10; //报错:未定义标识符
b = 10;
c = 10; //报错:未定义标识符
d = 10;
//a和c都为局部变量(作用域在变量定义到当前函数结束)
}
总结:
- 包含全局或静态的变量:生命周期皆为程序开始到程序结束,否则生命周期为当前语块(存储于静态全局区)
- 静态全局区:从程序开始系统自动分配内存,到程序结束系统自动回收内存
4 栈区
存储普通局部变量, 从定义开始系统自动分配内存,出了函数系统自动回收临时内存
5 堆区
由程序员手动申请,手动释放
二 void* 指针
空类型指针
#include<stdio.h>
int main() {
void* p = NULL;
p + 1; //错误:表达式必须是指向完整对象类型的指针
p++; //错误:表达式必须是指向完整对象类型的指针
int* pn = NULL;
pn = p;//错误:不能将"void*"类型的值分配到"int*"类型的实体
p = pn;//正常执行
short* psh = NULL;
p = psh;
*p; //错误
return 0;
}
1 不能偏移
2 不能自增自减
3 可以接受任意其他类型的指针
4 不能直接给其他类型的指针值(可以强转)
5 不能直接取内容
void*类型的指针不知道自己的长度(不完整)
三 动态申请内存
-
申请释放的方法
头文件:
#include<stdlib.h>
申请
void* malloc(size_t _Size); // 参数:要在堆区申请的字节数 void* calloc(size_t _Count, size_t _Size);// 参数:类型长度, 个数
释放
void free(void* _Memory); // 参数:地址(释放内存的首地址)
内存申请之后必须释放. 释放之后指针必须置空.
-
内存泄露和野指针
内存泄露: 申请的内存没有进行释放
野指针: 指针指向了不该指向的地方
-
简单应用举例
1.1 使用malloc申请一个int类型大小的内存(4字节)
//1. 申请 int* p = (int*)malloc(sizeof(int));//void* 给值要进行强转 //2. 使用 *p = 100; //3. 释放 free(p); //4. 置空 p = NULL;
1.2 使用malloc申请十个int类型大小的内存(40字节)
//1. 申请 int* p1 = (int*)malloc(sizeof(int) * 10); //2. 使用 if (p1) { //未申请成功,函数返回NULL 由此判断是否申请成功 //指针偏移使用 p1[0] = 1; p1[2] = 10; } else printf("申请失败"); //3. 释放 free(p1); //4. 置空 p1 = NULL;
2.1 使用calloc申请一个int类型大小的内存(4字节)
//1. 申请 int* p2 = (int*)calloc(sizeof(int), 1); //2. 使用 *p2 = 100; //3. 释放 free(p2); //4. 置空 p2 = NULL;
完整代码
#include<stdio.h> #include<stdlib.h> int main() { //1.1 使用malloc申请一个int类型大小的内存(4字节) //1. 申请 int* p = (int*)malloc(sizeof(int));//void* 给值要进行强转 //2. 使用 *p = 100; //3. 释放 free(p); //4. 置空 p = NULL; //1.2 使用malloc申请十个int类型大小的内存(40字节) //1. 申请 int* p1 = (int*)malloc(sizeof(int) * 10); //2. 使用 if (p1) { //未申请成功,函数返回NULL 由此判断是否申请成功 //指针偏移使用 p1[0] = 1; p1[2] = 10; } else printf("申请失败"); //3. 释放 free(p1); //4. 置空 p1 = NULL; // 使用calloc申请一个int类型大小的内存(4字节) //1. 申请 int* p2 = (int*)calloc(sizeof(int), 1); //2. 使用 *p2 = 100; //3. 释放 free(p2); //4. 置空 p2 = NULL; return 0; }
-
动态数组
动态数组(并非数组,而是堆区申请的内存)
类一维数组
#include<stdio.h> #include<stdlib.h> int main() { //1. 申请 int len = 10; int* arr = (int*)malloc(sizeof(int) * len); //2. 使用 if (arr) { //未申请成功,函数返回NULL 由此判断是否申请成功 //指针偏移使用 for (size_t i = 0; i < len; i++) { arr[i] = i; } for (size_t i = 0; i < len; i++) { printf("%2d",arr[i]); } printf("\n"); } else printf("申请失败"); //3. 释放 free(arr); //4. 置空 arr = NULL; return 0; }
类二维数组
#include<stdio.h> #include<stdlib.h> int main() { // 申请类似于int arr[3][4]; //1. 申请 //1.1 申请3个指针 int** pp = (int**)calloc(sizeof(int*), 3); //函数参数用一级指针,函数应返回二级指针 //1.2 分3次申请4个int大小的内存 for (size_t i = 0; i < 3; i++) { pp[i] = (int*)calloc(sizeof(int), 4); } // ==> 不一定像真正的二维数组一样内存连续 //2. 使用 for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 4; j++) { pp[i][j] = i * 10 + j; printf("%3d", pp[i][j]); } printf("\n"); } printf("\n"); //3. 释放 free(pp); //4. 置空 for (int i = 0; i < 3; i++) { pp[i] = NULL; } return 0; }
简单数组扩容
思路:
-
给定默认长度
-
添加数据
-
如果装满了,自动扩容
#include<stdio.h> #include<stdlib.h> int main() { // 1. 给定默认长度 // 2. 添加数据 // 3. 如果装满了,自动扩容 // 默认初始长度:10 int len = 10; // 申请长度为len(10)个int大小的内存 int* p = (int*)calloc(sizeof(int), len); int* ptemp = p; // 保存p当前地址 int i = 0; // 下标 int num = 0; // 用来接收每一次的输入 // 添加数据(重复输入)_结束标志: -1 while (scanf_s("%d", &num), num != -1) { // 1 长度足够 if (i < len) { ptemp[i++] = num; } // 2 长度不够,需要扩容 else { // 自定义扩容规则: 变为原来2倍 len *= 2; p = (int*)calloc(sizeof(int), len); //重新申请一段内存 // 拷贝之前内存中的数据 for (size_t j = 0; j < i; j++) { p[j] = ptemp[j]; } p[i++] = num; //添加当前需要添加的数据 free(ptemp); ptemp = p; //ptemp 重新指向新内存 } } // 检验 for (size_t j = 0; j < i; j++) { printf("%3d",ptemp[j]); } printf("\n当前内存长度:%d\n当前数据个数:%d\n",len,i); free(p); //释放 // p和ptemp为同一段内存,切勿重复释放 p = ptemp = NULL; return 0; }
-