指针、引用

内存

1.什么是内存?

  • 内存也叫主存储器,用于临时存储数据和程序指令,便于CPU(也就是处理器)快速访问

    • 读写非常快,对比硬盘等外存,一秒一般 \(10^8\) 次读写

      int main() {
      	//clock_t 是一个用来表示CPU时钟的变量  clock()函数是获取程序运行时间的函数
      	clock_t start_t=clock(); // 记录开始时间start_t
      	clock_t time; //运行时间time
      	long long cnt = 0;
      	while(true)
      	{
      		clock_t end_time=clock();  //每层循环都记录一次时间
      		time = end_time - start_time; //用开始的时间减去当前时间就是CPU运行时间
      		if((double)(time)/CLOCKS_PER_SEC >= 1)break;//如果时间大于一秒,就break。
      		 											//CLOCKS_PER_SEC是一个单位
      		 											//这个时间time单位还是毫秒所以得换算一下单位
      		cnt++;
      	}
      	
      	cout<<(double)(time)/CLOCKS_PER_SEC<<"s"<<endl;//输出时间
      	cout<<cnt<<endl;
      	
      	return 0;
      }
      
    • 在电脑突然断电后,内存数据会丢失,数据库跑任务突然断电有丢失数据的危险,现在的数据库都有数据库管理系统来防止突然断电的情况

    • 地址空间以字节为单位,每个字节都有自己独立的地址

image-20241031193934562

这是内存的大致表示图,每个字节都是以十六进制表示出来的。0x 是一个前缀,代表后面的是十六进制的数

2.各个数据类型所占用的空间大小:

int类型:4字节

long long类型:8字节

double类型:8字节

float类型:4字节

bool类型:1字节

char类型:1字节

汉字字符类型:不同编码方式大小不同,UTF-8编码中占3字节,GBK编码中占2字节

cout<<sizeof(int)<<'\n';
cout<<sizeof(long long)<<'\n';
cout<<sizeof(double)<<'\n';
cout<<sizeof(float)<<'\n';
cout<<sizeof(bool)<<'\n';
cout<<sizeof(char)<<'\n';
cout<<sizeof("a")<<'\n';

sizeof函数:

获取大小

memset(arr,0,sizeof(arr));

memset(arr,0x3f,sizeof(arr));//注意,初始化值是以字节为单位把每个字节初始化为0x3f

memset(arr,0x3f,sizeof(arr));
for(int i = 0;i < 100;i ++) 
	cout<<arr[i]<<" ";
cout<<endl;
cout<<"0x3f3f3f3f代表的值为:"<<0x3f3f3f3f<<endl;

image-20241103200518117

image-20241103204224606

3. 内存分区以及程序运行过程中,各个分区占用情况

内存可以分为5个区域

image-20241031192743751

  • 栈区

    用于放局部变量、参数、返回地址,在函数结束时释放

  • 堆区

    用于动态分配内存,由我们手动释放,比如mallocnew

  • 数据段

    存放已初始化的全局变量和静态变量

  • BSS段

    存放未初始化的全局变量和静态变量

    在程序加载时自动初始化为0

  • 代码段

    存储计算机需要执行的代码,也就是我们写的程序

如何动态分配内存?

在C语言中,我们学过用malloc动态分配内存,c++里面我们可以用new一段内存出来

int *x = new int;  //为指针b分配了一个整型的空间
int *array = new int[100] //新建了一个数组大小为100的整型数组

如何释放分配的内存?

注意在不需要使用内存或者函数结束的时候需要我们手动释放内存,这就需要delete函数来释放内存了

delete x;
delete[] array;

指针

  • 指针是用于存储其他变量的地址的变量

    int a = 10;
    int *p = &a; //&取地址
    

image-20241031195127130

  • * 符号 ,解引用

    • 号的使用主要在两个地方,声明指针的时候和对指针解引用的时候
    什么是解引用?

    解引用就是访问指针所指向的地址的数据

  • 通过指针解引用来访问被指向的变量或者变量的地址

    int a = 10;
    int *p = &a;
    *p = 100;
    cout<<a<<endl;
    
  • 指针也能用来指向一段内存空间

    int *arr = new int[100];
    arr[10] = 2;
    
  • 为什么指针算数和数组是等价的

    指针算数:

    int p[100];
    *(p + 10) = 2;
    cout<<*(p + 10)<<endl;
    

    数组:

    int p[100];
    p[10] = 2;
    cout<<p[10];
    

    等价的原因是在c++内部处理指针算数的方式

    我们对一个指针变量+1,指针增加的数量等于它指向的变量类型的字节数,在数组中的表现就是从p[0] 增加了一个整型的空间 变成了p[1];

    *(p + 10)就是增加了10个整型空间变成了p[10]

    image-20241031211753404

  • 对数组取地址

    int arr[100];
    cout<<arr<<endl; //这个输出的arr[0]
    cout<<&arr<<endl; //这个输出的是整个数组的地址,
    				  //虽然输出的地址和arr[0]的地址是一样的,在含义上它代表的是整个数组的地址
    

    *arr指针的数据类型是int **

    &arr的数据类型是 int (*) [10]

    两者虽然输出一样但是含义不一样

  • 野指针

​ 野指针是指没有初始化的指针变量,没有指向合法的内存空间

  • 悬空指针

    悬空指针是指针指向的变量被delete掉了

 int *p = new int;
 int *pp = p;
 delete p;

这里我们为p分配了一个int大小的空间,让pp指向指针p,然后我们把这段空间释放掉,pp就没有指向了,成为了悬空指针,也是一种野指针

  • 指针数组

    int* p[10];
    int a = 100,b = 20,c = 30;
    p[0] = &a,p[1] = &b,p[2] = &c;
    

    和int p[10] 一样,int* 表示的后面数组p[10]的数据类型,数组每一个位置都是一个指针

  • 数组指针

    数组指针就是指向数组的指针

    屏幕截图 2024-11-02 113825

    int arr[20][20];
    int (*p)[10] = arr;//意思是p这个指针指向的空间大小是10
    

    这个与指针数组的区别就是在* p这里多了一个括号,但是含义就差别很大了

  • 指针函数

    指针函数是返回值是指针的函数,有了这个我们可以方便的动态分配内存给数组

    int* getArray(int size)
    {
    	int *arr = new int[size];
    	return arr;
    }
    
  • 函数指针

    #include<iostream>
    using namespace std;
    
    int add(int a,int b) // 两数相加函数
    {
    	return a + b;
    }
    
    int subtract(int a,int b) //两数相减函数
    {
    	return a - b;
    }
    
    int multiply(int a,int b)//两数相乘函数
    {
    	return a * b;
    }
    
    
    
    int main() {
    	
    	int (*p)(int ,int) = NULL;
    	char c;
    	cin>>c;
    	switch(c)
    	{
    	case('-'):
    	p = subtract;
    		break;
    	case('+'):
    	p = add;
    		break;
    	case('*'):
    		p = multiply;
    		break;
    	};
    	
    	int a,b;
    	cin>>a>>b;
    	cout<<p(a,b);
    	
    	return 0;
    }
    

    函数指针定义格式是:

    函数类型 ( 指针名) (指向的函数的数据类型)* 以函数int add(int a,int b)举例,指针就是 int ( p) (int, int) = add;*

    和指针函数的区别:

    • 和指针函数相比多了个括号 如果少了括号就容易被识别成函数指针
    • 括号里面只有变量类型,没有变量名

引用

  • 概念:引用是为已存在的变量取了一个别名,引用引用的变量共用同一块内存空间**

    例子:int &a = b;后面直接可以用a代替b;

  • 引用的性质

    • 引用在定义的时候必须初始化
    • 一个变量可以有多个引用
    • 一个引用也能有引用
    • 引用一旦引用一个实体,不能再引用其他实体
    • 可以对任意类型进行引用
    引用在定义时必须初始化
     int &b;
     int a = 10;
     b = a;
    

    上面这种是错误的

    int a = 10;
    int &b = a;
    

    我们在定义的时候就要初始化

    一个变量可以有多个引用
    int a = 10;
    int &b = a;
    int &c = a;
    
    一个引用也能有引用
    int a = 10;
    int &b = a;
    int &c = b;
    

    一个引用也能被引用

    引用一旦引用一个实体,就不能再更改
    int a = 10,c = 20;
    int &b = a;
    b = c;
    

    这种是错误的

    可以对任意类型进行引用

    以函数的引用为例子,我们这里要引用一个add加法函数,它的两个参数都是int类型,我们定义函数引用的时候也要遵循函数的参数类型

    int add(int a,int b)
    {
    	return a + b;
    }
    int main()
    {
    	int (&p)(int,int) = add;
    	return 0;
    }
    

    函数引用的定义方式和我们的函数指针类似,都是括号里面指针名或者引用名,后面再加上函数的变量类型

posted @ 2024-11-03 21:22  chhh31  阅读(70)  评论(0编辑  收藏  举报