大家好啊,这里是c++之旅第二弹,跟随我的步伐来开始这一篇的学习吧!
如果有知识性错误,欢迎各位指正!!一起加油!!
创作不易,希望大家多多支持哦!
一、内存四区
在系统为程序开辟内存时,为了方便分配和管理内存,将程序的内存区域划分为4块,分别为:
栈区:存放函数的形参、局部变量等。由编译器自动分配和释放,当函数执行完毕时自动释放。
堆区:用于动态内存的申请与释放,一般由程序员手动分配和释放,若程序员不释放,则程序结束时由操作系统回收。
全局静态常量区(全局区):存放常量(一般是字符串常量和其他常量)、全局变量和静态变量,在程序结束后由操作系统释放。
代码区:存放可执行的代码。
数据区:包括栈区,堆区,全局静态常量区
代码区作用示例:
定义了一个结构体类型,然后在主函数中使用该类型定义一个变量,那么从底层角度来看系统是怎么知道这个结构体类型有什么基本数据类型从而进行使用呢?这就是代码区的作用了,因为定义结构体的代码会存储到代码区,用于让系统知道结构体内的数据类型。
二.C语言中的动态内存开辟:
1.什么是动态内存开辟:
动态内存开辟就是使用户可以根据自己的需要,向系统申请所需大小的内存空间并通过指针进行管理
2.C语言中的动态内存开辟:
以下函数使用时都需要包含头文件stdlib.h。
(1)使用malloc函数:
malloc(int size);//用于分配一个大小为size的内存区域。
(2)使用colloc函数:
calloc(unsigned int n,int size);//用于分配n个大小为size的连续内存区域,并初始化为0
(3)使用realloc函数:
realloc(void * p,unsigned int size);例如:用于重新分配(追加)已经通过malloc函数或calloc函数开辟的内存空间,可以改变其内存空间的大小
(4)使用free函数:
void free(void * p);
释放的时候,指针必须指向该堆区内存的首地址
用于释放指针变量p所指向的动态内存空间,使得这部分空间能被其他变量使用,否则这段内存空间需要等到程序结束后才会被释放。
每次使用完动态内存空间的时候需要释放内存空间。如果不释放,会导致内存泄漏(以后无法使用此空间)。内存泄漏会导致程序运行时占用的内存逐渐增加,直到达到系统可用内存的上限。
示例:
int *p = (int*)malloc(sizeof(int) * 10); //申请10个int大小的内存
for (int i = 0; i < 10; i++)
{ *(p + i) = i + 1;
}
for (int i = 0; i < 10; i++)
{ printf("%d\t", *(p + i));
} printf("\n");
//1 2 3 4 5 6 7 8 9 10
//正常的输出
p = (int*)calloc(5, sizeof(int)); //重新分配5个int大小的内存,并初始化为0
for (int i = 0; i < 10; i++)
{ printf("%d\t", *(p + i));
}
printf("\n");
//0 0 0 0 0 -33686019 -86947674 7843 13418400 13388992
//前面5个是正常的,后面5个越界访问,所以是未知的值
//越界访问是不可取的操作,越界操作的内存,你可以认为是不属于你的东西,不属于你的那么你就不应该去使用它
realloc(p, sizeof(int) * 10); //重新分配10个int大小的类型
for (int i = 0; i < 10; i++)
{ printf("%d\t", *(p + i));
}
printf("\n");
//0 0 0 0 0 -842150451 -842150451 -842150451 -842150451 -842150451
//前面5个是正常的,后面5个没有初始化,所以是未知的值,并且由于前面五个都是0,说明是追加的函数
free(p);//free函数使用时必须指针指向首地址,否则会报错。
C语言中malloc和calloc函数本质上是一样的,可以混着用。
realloc函数追加的内存不一定是和原来内存连续的,而有可能是逻辑上的连续,因为在使用完开始的内存后,后面的内存也可能会继续被其他代码使用。
三、C++中的动态内存开辟:
1.new和delete:
new在c++中相当于C语言中的malloc,delete相当于C语言中的free
C语言中的是函数,C++中的开辟内存使用的是关键字或者运算符
new可以开辟各种数据类型,结构体也可以
为了避免指针使用时的错误,如下细节需要注意到:
(1),定义指针初始化为空值(NULL)
(2),记得在使用完指针开辟的内存后写delete释放内存,否则会造成内存泄漏,积累会导致堆区内存越来越小
(3),写完delete之后再赋值为空
注意点:指针变量大小是四个字节(一般),因为一般是32位系统。
new了一个连续内存时,赋值操作是通过循环解引用指针来进行赋值的
四,结构体指针:
结构体指针的使用方式:
1:定义结构体指针指向结构体变量
例如:假设有结构体类型为student
student stu;
student *p1=&stu;
2:定义堆区的结构体类型内存来通过结构体指针来进行管理
student *p2=new student;
3.在定义结构体变量时,C语言中struct关键字是不可以省略的,而在c++中则可以省略,直接用结构体名字
五,多重指针(分为多级指针和多维指针,目前讲多级指针):
1.使用举例:
void main(){int *p=new int[10];int **p1=&p;for(int i=0;i<10;i++){*(p+i)=i+1;}delete[] p;}
以上的赋值操作可以使用二级指针来进行代替操作:
for(int i=0;i<10;i++){*(( *p1)+i)=i+1;}
2.写法一点注意点:
在c++和C语言中size_t是unsigned int 类型的宏替换
3.使用函数及二级指针来进行堆区内存的开辟(为什么使用函数开辟内存时要使用二级指针来进行内存的开辟呢,因为此时使用一级指针相当于之前的值传递而非地址传递)代码示例:
void fun(int **p,size_t n){*p=(int *)malloc(n);}void main(){int *pArr=NULL;fun(&pArr,10*sizeof(int));free(pArr);}
4.使用多级指针来写动态二维数组:
动态变化的二维数组首先想到的是往数组中括号内填入变量,通过改变变量的值实现动态变化,但填入变量是不支持的,所以想到二级指针。
int row=3,col=4;int **pArr=new int *[row];for(int i=0;i<row;i++){*(pArr+i)=new int [col];}for(int i=0;i<row;i++){for(int j=0;j<col;j++){*( *(pArr+i)+j)=i *col+j+1;}}for(int i=0;i<row;i++){for(int j=0;j<col;j++){printf("%d\t",pArr[i] [j]);}printf("\n");}