C++启蒙笔记(三)---指针

一、指针

  • 程序员眼中的内存如下
    在这里插入图片描述

1.1 地址

  • 释义:每一个格子代表1字节,即8bit,可以存储一个字母,每一个格子都有一个编号,即地址,上图中简略表示成格子下面的数字,通常为16进制,类似0x7fffde585878这样,CPU会根据这个编号通过一定的方式调用内存中指定位置的数据(这些都是高级语言虚拟出来的,硬件中并不存在

1.2 指针类型

  • 释义:一种数据类型,例如int* 表示整型指针类型,跟int,float等为同一等级
  • 初始值:指针本身是一个对象,生命周期内可指向不同的对象,无须在定义时赋初值,但在块作用域内为不确定值,建议赋初值nullptr

1.3 指针变量(通常也叫指针)

  • 常规使用
    // 定义、初始化整型变量num
    int num = 3;		
    // 定义多个整型指针变量pint
    int *pint, *pint_temp;				
    // 赋值整型指针变量,&为提取变量地址,然后存到pint这个整型指针变量里
    pint = #			
    	
    // 以下三种写法都可:定义、初始化指针变量pint
    int* pint = #		
    int *pint = #	// 常用
    int * pint = #		
    
    • 普通变量地址:int默认为4个字节,系统会在内存中取出4个字节组成来存放整型数据3,并以该字节块首字节的地址作为num的地址
    • 取用数据*pint,取用时,会根据指针判断数据类型及num的地址,然后直接读取出对应的内存字节块内的数据并返回
    • pint叫法:pint为指向num的指针(变量),通常变量两个字省略,pint也称为一级指针,保存变量num的地址

  • void指针
    • 概述:void为特殊的指针类型,可存放任意对象地址
    • 功能:只可比较(==或!=),作为函数的输入输出,赋给另外一个void指针,不可提取指针所指的对象
      // 定义:此处obj可以是任何类型
      void *pv = &obj;
      

1.4 指向指针的指针

  • 定义:若一个指针(变量)存储了一个指针(变量)的地址,就称这个指针为指向指针的指针,也称为二级指针,保存的是一个指针(变量)的地址
  • 代码释义
    int var,var_0;
    // 一级指针,保存一个整型变量的地址
    int *ptr;		
    // 二级指针,保存一级指针的地址		
    // 变量pptr前面的修饰都是为了解释说明变量pptr
    // 它指向的必须是一个指针,它存的也必须是指针的地址
    int **pptr;			
     
    var = 3000; 
    // 使用取地址运算符&,提取变量var的地址
    ptr = &var;			 
    // 使用解除引用运算符*,提取指针指向的变量的值   
    var_0 = *ptr;			
    // 使用取地址运算符&,提取指针变量ptr的地址
    pptr = &ptr; 			
    

1.5 const与指针

1.5.1 常量指针

  • 特点:指针可变更,指针指向的对象不可变更
  • 定义:const int * pt;强调*pt是固定的
  • 示例
    int age = 29;
    int weight = 160;
    // 意义:对于pt来说,pt认为*pt是一个常量,即不可以通过pt指针这个途径改变age的值
    const int * pt = &age;		
    
    // 以下可以,pt的声明并不意味着它指向的值实际上一定是一个常量
    age = 33;			
    // 以下可以,pt可以指向另一个变量		
    pt = &weight;	
    // 以下不可以,不可用通过pt指针这个途径修改age值			
    *pt = 33;					
    
    // 对比记忆
    // 普通常量声明及定义
    const int age = 29;			
    // 以下初始化不可以,因为*pt为非常量
    // 若成立,代表可以用pt修改age的值,与上一句矛盾
    int *pt = &age;				
    
  • 函数中应用
    // 函数原型:也叫函数声明
    int arr(const int * pt);	
    
    int week[4] = {1,3,5.7};
    // 将数组指针传给函数体,但是函数体不可以通过这个指针修改数组里的数据
    arr(week); 					
    

1.5.2 指针常量

  • 特点:指针pt是固定的,即指针中存的变量地址是固定的,但变量值*pt是可变的
  • 定义:int * const pt;强调pt是固定的
  • 示例
    int age = 29;
    int weight = 160;
    // 意义:对于pt来说,pt认为pt是一个常量,即不可以改变pt指向的内存地址
    int * const pt = &age;			
    
    // 不可以,pt只能指向age对应的内存地址
    pt = &weight;		
    // 可以,可以通过pt这个途径改变age的值			
    *pt = 33;						
    

二、 存储方式

2.1 栈式存储方式

  • 特点
    特点解释
    静态联编变量(普通变量、数组、结构体等)在程序编译时就加入内存中,不管程序是否使用
  • 写法
    // 值300存储在栈中
    int num = 300;		
    // 指针(变量)ptr存储在栈的内存区域中	
    int * ptr = #		
    

2.2 堆式存储方式

  • 特点
    特点解释
    动态联编变量(普通变量、数组、结构体等),程序运行阶段,若需要则在内存中创建它,否则不创建
    多次操作不可释放已经释放的内存块,也不可将两个指针指向同一个内存块
    空指针nullptr,使用nullptr会导致程序崩溃,但是delete nullptr;不会有任何影响
    释放堆内存delete专注的是释放指向的内存空间:
    ptr_1 = ptr; delete ptr_1;语句,跟delete ptr; 功能一样,注意不可释放两次内存
  • 写法
    // new关键字:指针(变量)ptr的值存储在栈的内存区域中,其指向堆的内存区块
    int * ptr = new int;	
    // 将值300存储在堆中,不可用ptr = &num这种赋值,因为这种的num放在栈中
    *ptr = 300;				
    
    // ......
    
    // 释放指针所指向的内存空间,否则内存泄露
    delete ptr;				
    

三、应用

个人理解:

  • 涉及到指针定义、初始化的语句,如:int * pt = &x;
  • 等号左侧:除指针pt外,其他所有都是在解释变量pt,即它指向数据类型,以及能存储数据类型
  • 等号右侧:是进行初始化工作,即提取x的地址值放到变量pt里
  • 等号左右:若类型不同,则会进行隐式转换报错

3.1 数组与指针

  • 常规数组及指针
    int a[3] = {1,3,5};
    
    // 返回首个元素的首地址,注意:sizeof(a[0])等于4
    int * pt1 = &a[0];				
    
    // 返回整个数组的首地址,注意:sizeof(a)等于12
    int * pt2 = &a[3];				
    
    // 输出0x7fff7aa19bfc,此为数组的首地址
    cout << &a << endl;		
    		
    // 输出0x7fff7aa19bfc,此为首元素的地址
    cout << a << endl;		
    		
    // 输出1,*解的是首元素的地址
    cout << *a << endl;				
    

    注:上式指针pt1内存储的地址等于pt2,但pt1中存储的类型为数组特定的元素,pt2中存储的类型为整个数组,从sizeof的结果可见

  • 数组指针:又名指向数组的指针
    在这里插入图片描述
    int a[3] = {1,3,5};
    
    // 优先级() > [] > *
    int (*pt)[3];					
    // pt为指向含有三个整型元素的数组的指针
    pt = &a;						
    // int (*pt)[3] = &a;等同于上面两句
    
    // 输出:0x7fff7aa19bfc
    cout << a << endl;				
    // 输出:0x7fff7aa19bfc
    cout << *pt << endl;			
    // 输出:1
    cout << **pt << endl;			
    
  • 指针数组:数组的每个元素的数据类型都是指针
    在这里插入图片描述
    int x = y = z = {};
    // 优先级() > [] > *,数组的每个元素数据类型由 int * 确定
    int * pt[3];					
    // x等于*pt[0]
    pt[0] = &x;						
    pt[1] = &y;
    pt[2] = &z;
    
    // 输出:0x7fffb3b2ca5c
    cout << pt[0] << endl;			
    // 输出:0x7fffb3b2ca58
    cout << pt[1] << endl;			
    // 输出:0x7fffb3b2ca54
    cout << pt[2] << endl;			
    

3.2 动态数组(堆式存储)

  • 写法
    // 创建动态数组,psome指向此数组的第一个元素
    int * psome = new int[3];		
    
    // psome[0] 等同于 *psome
    psome[0] = 10;					
    // psome[1] 等同于 *(psome+1)
    psome[1] = 100;					
    // psome[2] 等同于 *(psome+2)
    psome[2] = 1000;				
    
    // for循环功能,遍历数组元素
    for (int i = 0; i < 3; i++)		
    {
    	// 此处psome[0]  也可提取  psome指针指向的值  
    	cout << *psome << endl;		
    	// 将指针向下移动一个,注意:打印完psome[2]时,psome又加了一次
    	// 这种写法不好,容易把指针移丢,导致内存泄漏
    	psome += 1;					
    	
    }
    // 将指针移动回动态数组首位置,为下面释放内存做准备
    psome = psome - 3;				
    // 释放psome指针指向的整个数组的内存空间(3~5行),
    // 且指针psome需指向数组首位置
    delete [] psome;				
    

3.3 字符串

  • 赋值

    // 方法1:多次调用pchar,系统会创建多个副本
    // 所以编译器会强制用const关键字对字符串只读使用
    const char * pchar = "help";
    
    // 方法2:数组式赋值,不可越界,最多存9个可视化字符
    char pchar[10] = "help";	
    // 若指针指向字符串,则打印字符串,否则打印地址
    cout << pchar << endl;				
    >>>help						
    
  • 指针运算

    // 初始化:直接赋值
    char pchar[10] = "1234567890";		
    // 创建动态字符串(同数组)
    char * pchar_0 = new char[5];		
    char * pchar_1 = new char[5];
    
    // 浅拷贝:同类型赋值,仅拷贝地址,两个指针指向同一处内存
    // 且pchar_0失去对new的堆内存区间的控制
    pchar_0 = pchar;					
    
    // 深拷贝:将pchar的值拷贝到pchar_1指向的堆内存区间,最大长度4位
    strncpy(pchar_1, pchar, 4);			
    // 第5位用\0补齐
    pchar_1[5] = '\0';					
    
    // 打印字符串:指针pchar在cout语句中解释成字符串的值
    cout << pchar << endl;		
    // 打印字符串首地址:指针指向的字符串的首地址
    //(int* 为隐式强制类型转换)
    cout << (int*) pchar_0 << endl;		
    

3.4 动态结构

  • 定义结构体
    // 定义结构模板sth
    struct sth							
    {
    	char name[20];
    	int volumn;
    	double price;
    };
    
  • 定义结构实例
    // 定义、初始化结构(栈)
    sth stlist = {};					
    // 定义、初始化结构(堆)
    // 将创建的内存块的首地址赋值给变量(或叫指针)plist
    sth * plist = new sth;				
    
  • 输入
    // 动态结构项赋值(堆),只能收到19个,末尾加'\0'
    cin.get(plist->name,20);			
    // 常规结构项赋值(栈)
    cin >> stlist.volumn;				
    // 动态结构体赋值(堆)
    cin >> (*plist).volumn;				
    // 指针式动态结构体赋值(堆)
    cin >> plist->price;				
    
  • 输出
    cout << (*plist).name << endl;
    cout << stlist.volumn << endl;
    cout << plist->price << endl;
    
  • 释放堆内存
    // 释放堆内存块
    delete plist;						
    

上一篇:C++启蒙笔记(二)—数据类型
下一篇:C++启蒙笔记(四)—函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值