C语言------内存管理
- 系列文章目录
- c语言指针
- 项目开发常用字符串应用模型
文章目录
- 作用域
- 局部变量
- 全局变量
- extern全局变量声明
- 静态变量
- 静态(static)局部变量
- 静态(static)全局变量
- 未初始化
- 全局函数和静态函数
- 全局函数
- 静态函数(内部函数)
- 注意事项
- 内存布局
- 内存分区
- 存储类型总结
- 堆区内存分配和释放
- 1.第一种方式(设置)
- 2. 开辟堆空间存储数据
- malloc
- free
- 堆空间存储数据
- 内存操作函数
- 1) memset
- 2) memcpy
- 3) memmove
- 4) memcmp
- 内存常见问题
- 1)越界
- 2)开辟释放0个字节的空间
- 3)开辟内存空间能存2.5个整型
- 4)释放之后再释放
- 5)开辟和释放不是同一个指针
- 6)指针 值传递
- 解决办法1
- 解决办法2
- 二级指针对应的堆空间
作用域
作用域就是它能在哪去使用。生命周期就是从哪开始从哪结束,例如:夏虫朝菌、浮游朝生暮死、昙花一现······
C语言变量的作用域分为:
- 代码块作用域(代码块就是 { } 之间的一段代码)
- 函数作用域
- 文件作用域
局部变量
使用auto修饰,局部变量也叫auto自动变量(auto可写可不写),一般情况下代码块 {} 内部定义的变量都是自动变量,它有如下特点:
- 在函数内部定义的变量,只在函数范围内有效;
- 在复合语句中定义,只在复合语句中有效;
- 如果没有赋初值,内容为随机
- 局部变量未初始化,初值为随机值:C规范对该初值并没有做规定,具体实现由编译器决定。
- 全局变量未初始化,默认设置为初值。
- 局部未初始化变量,编译通过与否与具体编译环境相关。
- 作用域:在函数内部,从变量定义到函数结束。
- 生命周期:从变量定义到函数结束。
- 随着函数调用的结束或复合语句的结束局部变量的生命周期也结束
局部变量存在栈区。
复合语句(compound statement)简称为语句块,它使用大括号 { } 把许多语句和声明组合到一起,形成单条语句。
{ [声明和语句的列表] }
加不加auto都指的是局部变量,一般都不加auto进行修饰,跟声明extern一样不加。
#include <stdio.h>void Add(int a,int b)//形参也是局部变量
{int c = 0;//局部变量
}int main(void)
{/*在函数内部定义的变量 局部变量 作用域:在函数内部,从变量定义到函数结束生命周期:从变量定义到函数结束*/auto int a = 6;//定义变量 局部变量 for (int i = 0; i < 10; i++) {} //局部变量 i 作用域只限于for函数中//printf("%d\n", i);//errprintf("%d\n", a);//6return 0;
}
全局变量
- 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,需用 extern 声明;
- int j; 或者 extern int j;
不声明找不到
- 作用域:整个项目中所有文件,如果在其他文件中使用,需要声明,但不能重复定义,不同文件的全局变量不能重复。
- 全局变量的生命周期和程序运行周期一样;
- 生命周期:从程序创建到程序销毁。
- 全局变量存的不是栈区,
存在数据区。
- 不同文件的全局变量不可重名
- 局部变量可以和全局变量同名,不冲突,局部变量在栈区,是两个不同的变量,在操作的时候,数据是采取就近原则。
test1.c
#include <stdio.h>
//全局变量 在函数外部定义的变量
//作用域:整个项目中所有文件 如果在其他文件中使用 需要声明
//生命周期:从程序创建到程序销毁
int j = 1;void Func02()
{j = 666;printf("%d\n", j);
}
int main()
{printf("%d\n", j);//1Func02();//666Func03();//666return 0;
}
test2.c
#include <stdio.h>extern int j;//声明
void Func03()
{printf("%d\n", j);
}
如果不声明会报错,只有声明了编译才能通过。
#include <stdio.h>
int j = 1;int main()
{printf("%d\n", j);//1int j = 60;printf("%d\n", j);//60//匿名内部函数{ //这里叫代码体(程序体)//int j = 52;//第一种 ------------------------------j = 52;//第二种 ------------------------------printf("%d\n", j);//52} //程序结束之后这个函数就销毁了printf("%d\n", j);//第一种:60 ; 第二种:52return 0;
}
还可以打印地址看看printf("%p\n", &j);
extern全局变量声明
extern int a; 声明一个变量,这个全局变量在别的文件中已经定义了,这里只是声明,而不是定义。
静态变量
存储的位置:数据区
静态(static)局部变量
- static局部变量的作用域也是在定义的函数内有效,只能在函数内部使用
- static局部变量的生命周期和程序运行周期一样,从程序创建到程序销毁,同时staitc局部变量的值
只初始化一次,但可以赋值多次。
- 局部变量要优于函数,静态局部变量在
数据区
进行存储,程序在执行起来的时候,static这句话就已经走完一遍了 - static局部变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符型变量赋空字符
#include<stdio.h>void Func01()
{ //int y = 1;//局部变量static int y = 1;//静态局部变量 y++;printf("%d\n", y);
}int main()
{//静态局部变量//static int y = 1;//printf("%d\n", y);for (int i = 0; i < 10; i++){Func01();}return 0;
}
静态(static)全局变量
作用域:可以在本文件中使用,但是不可以在其他文件中使用
。
生命周期:从程序创建到程序销毁。
#include <stdio.h>
//静态全局变量
static int c = 1;
void Func()
{c = 666;printf("%d\n", c);
}int main()
{for (int i = 0; i < 10; i++){c++;printf("%d\n", c);}Func();
}
不可以在其他文件中使用静态全局变量,不然会报错
未初始化
局部变量未初始化在Visual Studio中是不允许使用的,因为虽然内存开辟空间了,但是打印的值都是乱码(任意值)
。在其他的编译环境中是允许使用的,Visual Studio做一个安全限制,你没有初始化它一定是错的,它会给你进行报错。
未初始化的全局变量:0
未初始化的静态全局变量:0
未初始化的静态局部变量:0
全局函数和静态函数
全局函数
在C语言中函数默认都是全局的(全局函数),extern可省略。项目中的所有文件都可以去调用它。
声明函数
extern 类型标识符 函数名( 形参列表 );
或者
类型标识符 函数名( 形参列表 );
其实不声明程序能找到
,但是如果你声明了你就可以右键跳转,找起来比较方便,声明是有一定意义的,声明一般是放在头文件中( .h文件),建议声明
。
C++有多态可以方法重载;C语言不支持一个函数名对应多个参数的样式,所以全局函数名称是作用域中唯一的。
作用域: 在整个项目中所有文件中使用。
生命周期:从程序创建到程序销毁
静态函数(内部函数)
在C语言中函数默认都是全局的,使用关键字static
可以将函数声明为静
态,函数定义为static就意味着这个函数只能在定义这个函数的文件中使
用,在其他文件中不能调用,即使在其他文件中声明这个函数都没用。
对于不同文件中的staitc函数名字可以相同。
static 类型标识符 函数名( 形参列表 );
作用域:当前文件中
静态函数可以和全局函数重名,当前文件中优先调用静态函数,就近原则。当静态函数和全局函数重名并且在同一文件中时,报错。
生命周期:从程序创建到程序销毁
#include <stdio.h>static void FuncStatic()
{printf("静态函数\n");
}int main()
{FuncStatic();return 0;
}
注意事项
- 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰。
- 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用。(就近原则)
- 所有的函数默认都是全局的,意味着所有的函数都不能重名,但如果是staitc函数,那么作用域是文件级的,所以不同的文件static函数名是可以相同的。
内存布局
内存分区
内存不止四区,内存四区只是对于我们应用程序来说的,内存四区模型图:
全局常量,安全的常量 ,存储区域为数据区常量区。局部常量是不安全的。
- 代码区:程序执行的二进制码(程序指令)。存放CPU执行的机器指令。另外,代码区还规划了局部变量的相关信息。
- 特点
- 共享
通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。 - 只读
代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。
- 共享
- 特点
- 数据区:加载的信息都是跟程序同生共死的
- 全局初始化数据区/静态数据区(
data段 或者 data segment
)
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。 - 未初始化数据区(又叫
bss区
)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为0或者空(NULL)。 - 常量区
- 全局初始化数据区/静态数据区(
- 栈区
(stack)
:系统为每一个程序分配一个临时的空间- 一般存局部变量、函数信息、函数参数、数组、指针、结构体。
- 栈区大小为1M,在windows中可以扩展到10M、在Linux中可以扩展到16M
- 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
- 存储变量的时候是
从高地址开始存
,向下增长(只有栈区是这样的)
,数组是例外首元素地址是从低地址开始。 - 函数入栈是从后向前入栈,例如函数 void func(int a,int b){} ,先存b再存a。
- 入栈是从高地址到低地址,出栈是从低地址到高地址,
先进后出、后进先出(沙桶原理)
- 堆区
(heap)
- 公共的区域
- 大小:理论是没有任何限制的,跟内存有关,除了上面三个区域剩下的全都是堆区,内存越大堆区越大,没有栈那样先进后出的顺序。用于动态内存分配。
- 存储大数据、图片、音频文件
- 需要手动开辟内存空间,malloc、colloc、realloc,是一块连续的空间,这个连续的空间会获取一个指针,这里面没有变量名,只能通过指针来操作。
- 需要手动释放,free
- 一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
- 从低地址开始存,向上增长
存储类型总结
#include <stdio.h>//全局常量 安全的常量 存储区域为数据区常量区
const int c1 = 123;
//未初始化的全局变量
int x1;
//初始化全局变量
int x2 = 52;
//未初始化的静态全局变量
static int s1;
//初始化静态全局变量
static int s2 = 16;int main()
{const int c2 = 123;//局部常量 不安全的int y = 10;//未初始化的局部变量static int j1;//初始化的局部变量static int j2 = 10;char* p = "hello xy";//字符串常量int arr[] = { 1,2,3,4 };//数组int** pp = arr;//指针printf("未初始化的全局变量: % p\n", &x1);printf("未初始化的静态全局变量: % p\n", &x2);printf("未初始化的静态全局变量: % p\n", &s1);printf("初始化静态全局变量: % p\n", &s2);printf("局部变量: % p\n", &y);printf("字符串常量: % p\n", p);printf("数组: % p\n", &arr);printf("指针变量: % p\n", pp);printf("指针地址: % p\n", &pp);return 0;
}
可以看到红色的存储区域都是数据区,里面又有一些区别,初始化的、未初始化、字符串常量。
绿色的是栈区,栈区一般存局部变量、数组信息、指针、结构体。
堆区内存分配和释放
栈区超出1M会报错,我们定义一个数组看看效果
#include <stdio.h>int main()
{int arr[1000000] = { 0 };//超出1M(默认),报错/*字节B 1000000*4=4000000KB 4,000,000/1024=3,906.25MB 3,906.25/1024=3.81*///int arr[1000000/4] = { 0 };//没有超出return 0;
}
1.第一种方式(设置)
默认是1M,如果想要扩充,通过程序编译之前,右键项目选择属性,windows中最大可改为10M,默认单位为byte,修改为10M,1010241024=10485760
#include <stdio.h>
int main()
{//栈区大小//int arr[1000000] = { 0 };//超出1M,报错/*字节B 1000000*4=4000000KB 4,000,000/1024=3,906.25MB 3,906.25/1024=3.81*/int arr[1000000/4] = { 0 };//没有超出return 0;
}
2. 开辟堆空间存储数据
malloc
#include <stdlib.h>
void* malloc(size_t size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的
连续区域
,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。
参数:
- size:需要分配内存大小(单位: 字节)
返回值:
- 成功:分配空间的起始地址,
- 失败:NULL
free
#include <stdlib.h>
void free(void* ptr);
功能: 释放ptr指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址,对同一内存空间多次释放会出错。
参数:
ptr:需要择放空间的首地址,被释放区应是由malloc函数所分的区域。返回值: 无
注意:开辟和释放必须是同一个指针
就算没有free(),main()结束后也是会自动释放malloc()的内存的,free()的用处在于实时回收内存。如果你的程序很简单,那么你不写free()也没关系。
#include <stdio.h>
int main()
{//开辟堆空间存储数据int* p = (int*)malloc(sizeof(int));printf("%p\n",p);//01685FE0printf("%d\n",*p);//-842150451//使用堆空间*p = 123;printf("%d\n", *p);//123//释放堆空间free(p);printf("释放完之后:%p\n", p);//01685FE0printf("释放完之后:%d\n", *p);//-572662307*p = 456;printf("%d\n", *p);//456return 0;
}
释放完还能操作这个堆空间地址吗?答案是可以,释放完p之后这个p就变成野指针了,p指向未知空间了,操作野指针对应的空间是可能报错的,可能也不报错的,我们尽量避免野指针的出现,可以在每次使用完p之后p=NULL;
把p置为空。
注意:开辟堆空间的时候,要有连续的空间,如果没有就失败了
#include <stdio.h>
#include <stdlib.h>
int main(void)
{//int* p = (int*)malloc(sizeof(int) * 1000000);//3M 打印的地址:00EE4040//int* p = (int*)malloc(sizeof(int) * 1000000 * 100);//300M 打印的地址:00EE4040//int* p = (int*)malloc(sizeof(int) * 1000000 * 1000);//3000M==2.92GB 打印的地址:00000000int* p = (int*)malloc(sizeof(int) * 1000000 * 1000 / 3);//1000M==1GB 打印的地址:01288040printf("%p\n",p);//free(p);return 0;
}
一般情况下, 加载到内存中的数据最多控制在1个G左右。
堆空间存储数据
#include <stdio.h>
#include <stdlib.h>
int main(void)
{//开辟10个内存大小int* p = malloc(sizeof(int) * 10);//开辟了40字节//一般不写//if (!p) {//p == NULL// printf("程序异常\n");// return -1;//}for (int i = 0; i < 10; i++){p[i] = i;}for (int i = 0; i < 10; i++){//printf("%d\n", p[i]);printf("%d\n", *(p+i));}free(p);return 0;
}
还可以放随机数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//随机数导入stdlib、time.h文件
#define MAX 10
int main()
{srand((size_t)time(NULL));int* p = (int*)malloc(sizeof(int) * MAX);for (int i = 0; i < MAX ; i++){p[i] = rand() % 100;printf("%d\n",p[i]);}free(p);return 0;
}
内存操作函数
1) memset
#include <string.h>
void * memset(void *s, int c, size_t n);
功能: 将的内存区域的前n个
字节
以参数填入,栈区堆区都可以
参数:
- s:需要剩作内存s的首地址
- c: 填的疗符.c虽然参数为int,但必须是unsigned char,范围为0~255
- n:指定需要设置的大小
返回值: s的首地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int* p = (int*)malloc(sizeof(int) * 10);//40//memset()重置内存空间的值//memset(p,0,40);//打印全是0memset(p,1,40);//打印全是16843009for (int i = 0; i < 10; i++){printf("%d\n",p[i]);}free(p);return 0;
}
一般都是用 memset(p,0,40); 重置成0。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char ch[10];//栈区memset(ch,'y',10);for (int i = 0; i < 10; i++){printf("%c\n",ch[i]);}return 0;
}
2) memcpy
#include <string.h>
void * memcpy(void *dest, const void * src , size_t n);
功能: 拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
- dest:目的内存首地址
- src: 源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错,内存里面一边往里读一边往里写,可能会导致内存被占用掉
- n:需要拷贝的字节数
返回值: dest的首地址
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{int arr[10] = { 0,9,1,2,3,4,5,6,7,8 };int* p = (int*)malloc(sizeof(int)*10);memcpy(p, arr, sizeof(int) * 10);for (int i = 0; i < 10; i++){printf("%d\n",p[i]);}free(p);return 0;
}
字符串拷贝strcpy 和 内存拷贝memcpy 的区别
- strcpy 字符串拷贝遇到\0停止,只会将第一个\0之前的内容拷贝过来。
- 内存拷贝memcpy 不管你是不是\0都拷贝,拷贝的内容和字节有关,和内容无关。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char ch[] = "hello\0 xy";char s[100];//strcpy(s,ch);//字符串拷贝遇到\0就停止memcpy(s,ch,9);for (int i = 0; i < 9; i++){printf("%c",s[i]);}return 0;
}
当拷贝源和拷贝目标发生重叠
#include <stdio.h>
#include <string.h>int main()
{int arr[] = {0,1,2,3,4,5,6,7,8,9};memcpy(&arr[5], &arr[3], 12);//0 1 2 3 4 3 4 5 8 9for (int i = 0; i < 9; i++){printf("%d ",arr[i]);}return 0;
}
0,1,2,3,4,5,6,7,8,9 ==> 0 1 2 3 4 3 4 5 8 9
3) memmove
memmove() 功能用法和 memcpy() 一样,区别在于:dest和src所指的内存空间重叠时,memmove() 仍然能处理,不过执行效率比memcpy() 低些。
如果源和目标重叠,它会自己在内存中开辟一块空间,先把你 源 挪到这去,然后再挪到目标文件,这样就不会报错了。
不重叠的时候跟memcpy一样
#include <stdio.h>
#include <string.h>int main()
{int arr[] = { 0,1,2,3,4,5,6,7,8,9 };//memcpy(&arr[5], &arr[3], 12);memmove(&arr[5], &arr[3], 12);//0 1 2 3 4 3 4 5 8for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
4) memcmp
#include <string.h>
void * memcmp(const void *s1, const void *s2, size_t n);
功能: 比较s1和s2所指向内存区域的前n个字节,比较的是内存中的值,不限于字符串、整型······
参数:
- s1:内存首地址s1
- s2:内存首地址s2
- n:需比较的前n个字节
返回值:
- 相等:=0
- 大于:>0
- 小于:<0
#include <stdio.h>
#include <string.h>int main()
{int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };int arr2[] = { 0,1,2,3,4,5 };int val= memcmp(arr1,arr2,20);printf("%d\n", val);//0return 0;
}
比较字符串:strcmp(s1,s2)只能比较 \0 之前的内容;memcmp可以比较\0之后,它比较的跟字节有关。
#include <stdio.h>
#include <string.h>int main()
{char s1[] = "hello\0 xy";char s2[] = "hello\0 xy";int val= memcmp(s1,s2,10);printf("%d\n", val);//0return 0;
}
内存常见问题
1)越界
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{//数组下标越界//char ch[8] = "hello xy";//堆空间数组下标越界char* p = (char*)malloc(sizeof(char) * 8);strcpy(p,"hello xy");printf("%s\n",p);/* 我开辟了8空间,但是我用了9个空间,释放是按照8释放还是9释放?按8释放没释放掉,按9释放释放多了释放多余的空间会出现问题 *///free(p);//一用就报错return 0;
}
2)开辟释放0个字节的空间
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(0);//野指针printf("%p\n", p);*p = 666;printf("%d\n", *p);//free(p);//一用就报错return 0;
}
3)开辟内存空间能存2.5个整型
一般情况建议用int* p = (int*)malloc(sizeof(int)*10);
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(10);p[0] = 0;p[1] = 11;//p[2] = 22;//一用就报错printf("%p\n", p);printf("%d\n", *p);printf("%d\n", *(p+1));free(p);return 0;
}
4)释放之后再释放
第一次释放是正确指针,第二次释放是野指针,释放野指针是不对的,操作野指针空间可能报错,释放野指针一定报错。堆空间不允许多次释放
我们用完给它置为空指针,就不会报错啦,堆空间不允许多次释放,空指针允许多次释放
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 10);free(p);p = NULL;//空指针free(p);free(p);free(p);free(p);free(p);return 0;
}
5)开辟和释放不是同一个指针
通过指针操作对应的堆空间的时候,尽量不要修改,如果需要修改最好做一个备份。
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(sizeof(int) * 10);printf("%p\n",p);for (int i = 0; i < 10; i++){//指针叠加 不断改变指针方向,释放会出错*p++ = i;}printf("%p\n", p);free(p);//这时候的p地址不是一开始的地址了return 0;
}
正确:备份
int main()
{int* p = (int*)malloc(sizeof(int) * 10);//printf("%p\n",p);int* temp = p;for (int i = 0; i < 10; i++){//指针叠加 不断改变指针方向,释放会出错//*p++ = i;*temp++ = i;}//printf("%p\n", p);free(p);//这时候的p地址不是一开始的地址了return 0;
}
6)指针 值传递
因为这两个p都是一级指针(同一级别的),虽然两个都是指针,但是指针传递的情况下,这种方式也是叫值传递。这种方式并没有堆空间地址传递过来。
#include <stdio.h>
#include <stdlib.h>void Func01(int * p)
{p = (int*)malloc(sizeof(int) * 10);
}int main()
{int* p = NULL;Func01(p);for (int i = 0; i < 10; i++)p[i] = i;free(p);return 0;
}
我们想要地址传递应该这样写:
解决办法1
用更高级的指针接收它
#include <stdio.h>
#include <stdlib.h>void Func01(int** p) //
{*p = (int*)malloc(sizeof(int) * 10);//
}int main()
{int* p = NULL;Func01(&p);//for (int i = 0; i < 10; i++)p[i] = i;free(p);return 0;
}
打印地址看看
解决办法2
返回值去接收
#include <stdio.h>
#include <stdlib.h>int* Func03()
{int* p = malloc(sizeof(int) * 10);return p;
}int main()
{int* p = NULL;p = Func03(&p);for (int i = 0; i < 10; i++)p[i] = i;for (int i = 0; i < 10; i++)printf("%d ",p[i]);free(p);return 0;
}
打印0 1 2 3 4 5 6 7 8 9。因为堆空间在函数Func03结束之后不会被释放,所以p操作堆空间不会报错。
二级指针对应的堆空间
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>int main()
{//int p[5][3];//开辟二级指针对应的堆空间int** p = (int**)malloc(sizeof(int*) * 5);//开辟5行3列的二维数组for (int i = 0; i < 5; i++){//开辟一级指针对应的堆空间p[i] = (int*)malloc(sizeof(int) * 3);}//存值for (int i = 0; i < 5; i++){for (int j = 0; j < 3; j++){scanf("%d", &p[i][j]);}}//取值for (int i = 0; i < 5; i++){for (int j = 0; j < 3; j++){printf("%d ", p[i][j]);}puts("");}//先释放里面的再释放外面的for (int i = 0; i < 5; i++){free(p[i]);}free(p);return 0;
}
注意:不是连续的堆空间
输入
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
C语言------内存管理相关推荐
- C语言内存管理内幕(二)----半自动内存管理策略
2019独角兽企业重金招聘Python工程师标准>>> C语言内存管理内幕(二)----半自动内存管理策略 转载于:https://my.oschina.net/hengcai001 ...
- C语言内存管理超详解
补充: 1.一个正在运行着的C编译程序占用的内存分为栈区.堆区.未初始化数据区(BBS).初始化数据区.代码区5个部分. (1)栈区:存放函数的参数值.局部变量的值.由编译器自动分配释放. (2)堆区 ...
- 【C语言重点难点精讲】C语言内存管理
文章目录 一:相关动态内存函数 (1)malloc和free (2)calloc (3)realloc 二:进程地址空间 三:常见内存错误 C语言内存管理其实是一个很糟糕的话题,很烦这个,但是没有办法 ...
- 分享:流言终结者——C语言内存管理
流言终结者--C语言内存管理 http://my.oschina.net/michaelyuanyuan/blog/104421
- C语言内存管理机制精讲-高手必修课视频教程-黄强-专题视频课程
C语言内存管理机制精讲-高手必修课视频教程-384人已学习 课程介绍 在企业级项目开发中一个非常重要的设计就是如何有效地管理内存资源.在C语言中,关于内存管理的知识点比较多,如函数变量 ...
- C语言文件联系人管理碎片整理,深入理解C语言内存管理.docx
深入理解C语言内存管理 之前在学Java的时候对于Java虚拟机中的内存分布有一定的了解,但是最近在看一些C,发现居然自己对于C语言的内存分配了解的太少. 问题不能拖,我这就来学习一下吧,争取一次搞定 ...
- 内存管理模拟程序c语言,C语言 内存管理详解
本文出自: 伟大的Bill Gates曾经失言: 640K ought to be enough for everybody -Bill Gates 1981. 程序员们经常编写内存管理程序,往往提心 ...
- C语言 内存管理之栈
内存管理之栈 什么是栈 栈管理内存的特点(小内存.自动化) 栈的应用:局部变量 栈的约束 关于栈的感悟 什么是栈 1.栈是一种数据结构,C语言中使用栈来保存局部变量.栈是管理内存的. 栈管理内存的特点 ...
- c语言malloc calloc,C语言内存管理:malloc、calloc、free的实现
任何一个对C稍稍有了解的人都知道malloc.calloc.free.前面两个是用户态在堆上分配一段连续(虚拟地址)的内存空间,然后可以通过free释放,但是,同时也会有很多人对其背后的实现机制不了解 ...
- C语言与JAVA内存管理_C语言内存管理
本章将介绍C语言动态内存管理. C语言编程语言提供了多种功能的内存分配和管理.这些函数可以在头文件中找到. S.N. 函数与说明 1 void *calloc(int num, int size); ...
最新文章
- python输入一个正整数n求下列算式的值_C语言编写程序:输入一个正整数x和一个正整数n,求下列算式的值。,C语言 编写一个程序,输入一个正整数,求出它是几位数。...
- KRSReader酷狗音乐歌词写真图片提取工具(提取KRS文件中的图片)
- [原创]Net实现Excel导入导出到数据库(附源码)
- java httpclient 进度条_如何使用Apache HttpClient 4获取文件上传的进度条?
- as3中使用字符串调用函数。
- PHP 销毁指定目录
- 领域驱动设计-从贫血模型到充血模型
- [渝粤教育] 辽宁对外经贸学院 国际集装箱多式联运 参考 资料
- 解决to_hdf() 报错ImportError: Missing optional dependency ‘tables‘. Use pip or conda to install tables.
- java连接navicat_如何使用Navicat连接Oracle
- java多维数组的反射类型_Java多维数组和Arrays类方法总结详解
- cocos3 动作回调函数
- 更改多个版本java_window下在同一台机器上安装多个版本jdk,修改环境变量不生效问题处理办法...
- 阿里巴巴Aliware十年微服务架构演进历程中的挑战与实践
- php transform,css transform属性怎么用
- 方舟手游服务器设置文件翻译,方舟生存进化单机模式设置中英文对照翻译一览...
- 百度市值要被京东超越了?你投百度还是京东?
- 交互设计 | 如何做到惊喜?
- 网易云音乐评论爬虫 params encSecKey逆向分析!
- 篇16:Windows安装配置Nessus时遇到的一些问题说明
热门文章
- sensei鼠标测试软件,「硬核测试:游戏鼠标精准度」赛睿SENSEI 310
- 最全面的openGL 入门学习
- 关于脚踝不得不说的各种事
- 深入理解计算机系统(2.5)---二进制整数的加、减法运算(重要)
- vue根据url获取内容axios_使用Vue.js和Axios从第三方API获取数据 — SitePoint
- 解决 LaTeX 中的中文显示问题
- 彩色matlab代码拷贝到word研究,matlab编辑器合并_彩色MATLAB代码拷贝到WORD研究
- 三种加快计算机启动速度的办法,电脑开机慢怎么解决?四种提速方法,前三种方法不花钱就能提速!...
- Xilinx的智能门控时钟技术你了解吗?
- 使用Tensorflow 2进行猫狗分类识别