动态内存

6.1 常用函数

函数名 全部单词 作用
malloc memory allocation 申请空间
calloc contiguous allocation 申请空间+数据初始化
realloc re-allocation 修改数据大小
free free 释放空间

在使用前,需导入 stdlib.h头文件

#include<stdlib.h>

6.1.1 利用malloc函数申请一片连续的空间

单位为字节

int* p = malloc(100);	// 100个字节
int* q = malloc(100 * sizeof(int));	//100个int类型

打印

printf("%d",*p);

6.1.2 赋值

第一种

#include<stdlib.h>
#include<stdio.h>

int main(){
    for(int i = 0; i < 100; i++){
        *(p + i) = (i + 1) * 10;	//10,20,30,40,50………
    }
  
    return 0;
}

第二种

#include<stdlib.h>
#include<stdio.h>

int main(){
    for(int i = 0; i < 100; i++){
        p[i] = (i + 1) * 10;	//另一种方式
    }
  
    return 0;
}

6.1.3 遍历

#include<stdlib.h>
#include<stdio.h>

int main(){
    for(int i = 0; i < 100; i++){
        *(p + i) = (i + 1) * 10;	//10,20,30,40,50………
    }
    //遍历
    for(int i = 0; i < 100; i++){
        printf("%d", *(p + i));
    }
    return 0;
}

6.1.4 用calloc函数申请并初始化(了解)

初始化值均为0

int* p = calloc(10 * sizeof(int));
int* q = calloc(10, sizeof(int));

6.1.5 用realloc函数扩容(了解)

需要传入待扩容的内存和扩容后的空间大小
原来的数据不会发生改变

int* pp = realloc(p, 200 * sizeof(int));

6.1.6 用free释放函数

一旦空间不再使用,请一定要释放掉

free(p);

6.2 一些细节

  1. malloc 创建空间的单位是字节
  2. malloc 返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
  3. malloc 返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小
  4. malloc 申请的空间不会自动消失,如果不能正确释放,会导致内存泄漏
  5. malloc 申请的空间过多,会产生虚拟内存
  6. malloc 申请的空间没有初始化值,需要先赋值才能使用
  7. free 释放完空间之后,空间中的数据叫做脏数据,可能是原来的值,可能被清空,可能被修改为其他值
  8. calloc 就是在 malloc 的基础上多一个初始化的动作
  9. realloc 修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原有的数据不会丢失
  10. realloc 修改之后,无需释放原来的空间,函数底层会进行处理

6.2.1 强转

不强转

/* void具有通用性,
可以转化为其他类型*/
void* p = malloc(100);

强转

int* p = malloc(100);
//int* :指针的步长
int* p = (int* ) malloc(100);

6.2.2 定义变量记录总大小

int* p = malloc(100);
int size = 100 / size(int);

遍历

#include <stdio.h>
#include <stdlib.h>
void method(int* p, int size);

int main(){
    int* p = malloc(100 * sizeof(int));
    int size = 100;
  
    //赋值
    for(int i = 0; i < 100; i++){
        *(p + i) = (i + 1) * 10;	//10,20,30,40,50………
    }
  
    //遍历
    method(p,size);
  
    //释放
    free(p);
  
    return 0;
}

void method(int* p, int size){
      for(int i = 0; i < size; i++){
        printf("%d ",p[i]);
    }
    printf("\n");
}

6.2.3 虚拟内存

在这个程序最终的结果中,申请空间的总大小可能大于电脑的总内存

虚拟:假的
当申请的空间过多,因为每一个内存空间不会再刚申请的时候就立马使用
所以C语言并不会立马就在内存中去开辟空间,而是什么时候存储数据了,才会真正的分配空间
目的:为了提高内存的使用效率

//一个遍历的代码
#include<stdio.h>
#include<stdlib.h>

int main() {

    //表示申请空间大小为1个G
    int number = 1024 * 1024 * 1024;
    int count = 0;
  
    while (1) {
		//如果申请空间成功,则会返回这个空间的首地址
		//如果申请空间失败,则会返回NULL
		int* p = (int*)malloc(number);
		count++;
		if (p == NULL) {
			printf("申请失败");
			break;
		}
		printf("内存%d申请成功:%p\n", count, p);
	}
  
	return 0;
}

6.3 C语言的内存结构

6.3.1 分类

  1. 代码区
  2. 初始化静态区
  3. 未初始化静态区
  4. 常量区

6.3.2 代码区

运行时代码加载到这里

6.3.3 栈

函数运行的区域
非全局变量和数组也在这里

6.3.4 初始化静态区

已初始化的static全局变量

6.3.5 未初始化静态区

未初始化的static全局变量

6.3.6 常量区

指针加双引号定义的字符串(字符数组)在里面

6.3.7 堆

用malloc, calloc, realloc 申请出来的空间会在堆里面

6.3.8 栈和堆的区别

  1. 堆的空间比栈大得多
  2. 栈内数组的生命周期与函数相关,随函数消失;堆中的数组在free之前永远存在,直到程序结束

6.4 变量数组在内存中的运行情况

int main(){
	int a = 10;
	int b = 20;
	int arr[] = {1,2,3};
	return 0;
}

在这个程序运行时
这串代码会被临时加载到代码区中进行存储,但不会运行
编译器会从上往下依次检查代码

当检查到main函数后,就会自动的调用
main函数会加载到里面,赋予一定的空间,开始执行里面的代码
a,b和长度为3的arr数组会被加载到main函数的空间里面

当main函数执行完毕后,main函数就会从里面出去
函数空间里面的所有变量和数组也会消失

6.5 全局变量和static变量在内存中的运行情况

int a = 10;

int main(){
	int* p = method();
	printf("%d\n",p);
	return 0;
}

int* method(){
	static int b = 20;
	return &b;
}

在这个程序运行时
这串代码会被临时加载到代码区中进行存储,但不会运行
编译器会从上往下依次检查代码

当检查到已赋值的全局变量a时
就会在初始化静态区中定义变量a

当检查到main函数后,就会自动的调用
main函数会加载到里面,赋予一定的空间,开始执行里面的代码

当main函数调用method函数时
method函数便会加载到当中
在method里面定义的static变量b会被加载到初始化静态区

return返回变量b的内存地址,该地址会被返回到method函数的调用处,即main函数
用指针p进行记录

此方法是为了使main函数在method函数结束后也能访问b的值

6.6 字符串在内存中的运行情况

int main(){
	char* str = "abc";
	printf("%s\n",str);

	str = "aaa";
	printf("%s\n",str);
	return 0;
}

在这个程序运行时
这串代码会被临时加载到代码区中进行存储,但不会运行
编译器会从上往下依次检查代码

当检查到main函数后,就会自动的调用
main函数会加载到里面,赋予一定的空间,开始执行里面的代码

char* str = "abc"是用指针加双引号定义的字符串
会被放在常量区中,无法被修改,可以被复用(若常量区有一个同样的值,便不会再次创建)
指针str被存储在main函数的空间中

char* str = "aaa"是用指针加双引号定义的字符串
会被放在常量区中,无法被修改,可以被复用(若常量区有一个同样的值,便不会再次创建)
指针str的值发生改变

6.7 malloc函数在内存中的运行情况

int main(){
    int* p = (int* ) malloc(100);
	*p = 10;
	printf("%d\n", *p);
  
    return 0;
}

在这个程序运行时
这串代码会被临时加载到代码区中进行存储,但不会运行
编译器会从上往下依次检查代码

当检查到main函数后,就会自动的调用
main函数会加载到里面,赋予一定的空间,开始执行里面的代码

malloc函数会被储存在
指针p被储存在main函数的空间中