1.全局变量与局部变量:
全局变量 :main函数执行之前创建,main函数执行结束,内存回收
局部变量:内存函数调用时,分配内存;函数调用结束,回收内存。
2.静态变量与非静态变量
区别:
void test() {
int a = 10;
++a;
printf("%d\n", a);
}
int main()
{
test(); //输出11
test(); //输出11
}
void test() {
static int a = 10;
++a;
printf("%d\n", a);
}
int main()
{
test(); //输出11
test(); //输出12
}
解释:非静态变量的空间在函数结束时销毁,静态变量的空间则不会,会在main函数执行结束之后销毁。
3.静态函数与非静态函数:
静态函数只能在当前文件中访问,而非静态函数可以在整个项目中任何的 .C 文件中访问。
以非静态函数为例:
在other.c类中:
void func() {
printf("hello world!\n");
}
在主类中可以直接调用:
void func(); //此步骤为声明函数
int main()
{
func(); //输出“hello world!”
}
PS.如果是 static void func():静态函数,则不可以跨文件访问。
4.内存分区
4.1内存分为代码区以及数据区。其中代码区用来放代码,剩下的变量放在数据区。
数据区分为:堆区,栈区,字符串常量区,全局/静态区:
(1)栈区:自动申请,自动释放。
1.函数的参数
2.函数内部定义的局部变量
3.可读可写
(2)字符串常量区: 编译器在程序执行之前分配内存,程序结束之后回收内存
1.双引号括起来的字符串
2.只读,不能修改
(3)全局/静态区:编译器在程序执行之前分配内存,程序结束之后回收内存
1.全局变量
2.静态全局变量
3.静态局部变量
4.可读可写
(4)堆区:手动管理内存,释放内存
1.根据需要申请任意大小的内存
2.根据需要选择在合适的时间释放内存(如果申请了没有释放,会造成内存泄漏)
申请内存用malloc(),释放内存用free().
int main()
{
//1.给int类型分配内存
int* p1 = (int*)malloc(4); //在堆上申请了4个字节的内存
*p1 = 30; //给这块内存赋值
printf("%d\n", *p1); //输出30
free(p1); //手动释放内存(此时内存就不能再使用了)
//2.给double类型分配内存
double* p2 = (double*)malloc(sizeof(double)); //开辟8个字节
*p2 = 3.14;
printf("%f", *p2); //输出3.140000
free(p2);
}
5.内存申请与管理
5.1整形
int g_a = 100; //全局区
void test01() {
int a = 100; //栈区
static int b = 20; //静态区
int* p = (int*)malloc(sizeof(int)); //堆区
*p = 30;
free(p);
}
5.2字符串
//字符串在堆与栈上的表示
void test02() {
char p1[] = "hello world!"; //字符串常量区
char* s = "hello world!"; //在栈区分配字符串
//在堆区分配内存
int length = strlen(p1) + 1; //考虑到结尾的/0
char* str = (char*)malloc(length);
strcpy(str, p1); //将字符串拷贝到str空间中
printf("%s", str);
free(str); //释放堆内存
}
5.3数组
//数组在堆与栈上的表示
void test03() {
int arr[] = { 10,20,30 }; //栈区
//堆区
int length = sizeof(int) * 3;
int* arr3 = (int*)malloc(length); //根据大小开辟空间
*arr3 = 100;
*(arr3 + 1) = 200;
*(arr3 + 2) = 300;
//遍历输出
for (int i = 0; i < 3; i++)
{
printf("%d", *(arr + i));
}
free(arr3); //释放堆内存
}
5.4按照需要来分配内存
//根据需要分配内存
void test04() {
int num = 0;
printf("请输入数组的长度");
scanf("%d",&num);
int* arr = (int*)malloc(sizeof(int) * num);
if (NULL == arr) {
printf("内存申请失败"); //malloc可能内存申请失败,申请失败就返回null,申请成功就返回内存首元素地址
}
//给数组元素赋值
for (int i = 0; i < num; i++)
{
*(arr + i) = 100 + 100 * i;
}
//遍历输出
for (int i = 0; i < num; i++)
{
printf("%d\n", *(arr + i));
}
if(arr!=NULL){ //先判断是否为空,不为空的情况下,再调用free函数
free(arr);
arr=NULL;
}
}
总结:
全局区:项目中所有的文件共享,变量或函数。
extern 类型,变量名
返回值类型,变量名
静态区:只要函数,变量加上static关键字,就只能在当前文件中访问。
字符串常量区:双引号括起来的字符串,不可修改
堆区与栈区:
堆区 | 栈区 |
内存由开发人员自己申请,自己释放,忘记free会 导致内存泄漏 | 内存由系统自动管理 |
内存比较大,大量的数据需要放到堆区 | 栈区比较小,大数据不要放上去 |
内存管理效率比较低 | 内存管理效率较高 |
ps.1.栈空间占用如果超过最大上限,会出现stack overflow
2.有些数据,我们需要控制它的生命周期,将数据放到堆上
3.堆区可以采用内存池进行优化,减少maollc和free的次数:
1.程序一运行,一次性malloc一大块内存
2.当程序需要运行时,找到自己的内存池使用
3.用完之后 放回内存池
6. 内存操作
头文件:#include<memory.h>
memset: 初始化内存
memcopy:内存拷贝,将内存中的字节拷贝到另一个内存,不能出现内存重叠。(字符串拷贝还是建议strcopy)
memmove:内存移动,可以处理内存重叠,但是效率低于memcopy
memcmp:内存比较
1.memset:三个参数分别代表初始化内存的首地址,将内存初始化为什么值,从首地址开始多少个字节
int main()
{
//1.以数组为例
int arr[10];
memset(arr, 0, sizeof(arr));
for (int i = 0; i < 10; i++)
{
printf("%d", *(arr + i)); //输出10个0
}
//2.以整形为例
int a = 410;
memset(&a, 0, sizeof(int));
printf("%d", a); //输出为0
//3.以字符串为例
char* s = (char*)malloc(sizeof(char) * 32);
memset(s, 0, 32); //输出为空
printf(" %s", s);
free(s);
}
2.memcopy与memmove
2.1 memcpy的使用
void test01() {
int a = 10;
int b = 20;
printf("%d %d\n", a, b); //输出10 20
memcpy(&a, &b, 4);
printf("%d %d\n", a, b); //输出20 20
}
2.2用memcpy交换两个变量的值
void test02() {
int a = 10;
int b = 20;
int temp = 0;
memcpy(&temp, &a,4);
memcpy(&a, &b,4);
memcpy(&b, &temp, 4);
printf("%d %d", a, b); //输出20 10
}
2.3理解内存,指针的类型
void test06() {
int a = 10;
const char* p = "abc"; //考虑到\0
memmove(&a, p, 4);
printf("%s", (char*)&a);
}
2.4memmove的使用方法
void test03() {
int arr[] = { 1,2,3,4,5 };
//遍历输出
for (int i = 0; i < 5; i++)
{
printf("%d\n", arr[i]); //输出1 2 3 4 5
}
//将后四位移动到前四位
memmove(arr, arr + 1, 4);
//遍历输出
for (int i = 0; i < 5; i++)
{
printf("%d", arr[i]); //输出2 2 3 4 5
}
}
3.memcmp的使用
void test05() {
int a = 10;
int b = 10;
if (memcmp(&a, &b, 4) == 0) {
printf("相等");
}
else {
printf("不相等");
}
}