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

本篇目录

  • 写在前面
  • 一、简述
    • 1. 地址:
    • 2. 指针类型:
    • 3. 指针变量(通常也叫指针)
    • 4. 指向指针的指针
    • 5.const与指针
      • 5.1 常量指针
      • 5.2 指针常量
  • 二、 存储方式
    • 1. 栈式存储方式
    • 2. 堆式存储方式
  • 三、应用
    • 1. 动态数组(堆式存储)
    • 2. 字符串
    • 3. 动态结构

写在前面

指针是C系列语言的重点和难点,搜到一个写指针很详细的博客,贴在这,方便以后复习,以下仅简略记录
C语言指针详解

一、简述

程序员眼中的内存如下
C++启蒙笔记(3)---指针_第1张图片

1. 地址:

每一个格子代表1字节,每一个格子都有一个编号,即地址,此处简略表示成下面的数字,通常为十六位,类似0x7fffde585878这样,CPU会根据这个编号通过一定的方式调用内存中指定位置的数据(这些都是高级语言虚拟出来的,硬件中并不存在)

2. 指针类型:

一种数据类型:例如int* 表示整型指针类型,跟int,float等为一个等级的集合

3. 指针变量(通常也叫指针)

存储两部分数据:指向的数据类型和变量地址

int num = 3;			// 声明并定义整型变量num
int * pint;				// 声明整型指针变量pint,明确指向的数据类型为int
pint = #			// 定义整型指针变量,&为提取变量地址,并存到pint这个整型指针变量pint里
	
int* pint = #		// 意义同下
int *pint = #		// 意义同下
int * pint = #		// 声明同时定义(推荐)
  • 普通变量地址:int通常是四个字节,系统会在内存中取出4个字节组成块来存放数据3,并以该字节块第一个字节的地址作为num的地址
  • 取用数据:*pint,取用时,会根据指针判断数据类型及num的地址,然后直接读取出对应的内存字节块内的数据并返回
  • pint叫法:pint为指向num的指针(变量),通常变量两个字省略,pint也称为一级指针,保存变量num的地址

4. 指向指针的指针

定义:若一个指针(变量)存储了一个指针(变量)的地址,就称这个指针为指向指针的指针,也称为二级指针,保存的是一个指针(变量)的地址

int var,var_0;
int * ptr;				// 一级指针,保存一个整型变量的地址
int * * pptr;			// 二级指针,保存一级指针的地址
 
var = 3000; 
ptr = &var;			    // 使用取地址运算符&,提取变量var的地址
var_0 = *ptr;			// 使用解除引用运算符*,提取指针指向的变量的值
pptr = &ptr; 			// 使用取地址运算符&,提取指针变量ptr的地址

5.const与指针

5.1 常量指针

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

5.2 指针常量

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

二、 存储方式

1. 栈式存储方式

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

2. 堆式存储方式

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

三、应用

个人总结:涉及到指针声明的同时赋值的语句,可以这样理解,例如int * pt = &x;等号左侧除指针pt外,其他所有都是在声明指针pt的数据类型,等号右侧是进行初始化的工作,即将&x的值放到指针pt里,若遇非同类型赋值,则会进行隐式转换或报错

1. 动态数组(堆式存储)

int * psome = new int[3];		// 创建动态数组,psome指向此数组的第一个元素

psome[0] = 10;					// psome[0]等同于*psome
psome[1] = 100;					// psome[1]等同于*(psome+1)
psome[2] = 1000;				// psome[2]等同于*(psome+2)

for (int i = 0; i < 3; i++)		// for循环功能,遍历数组元素
{
	cout << *psome << endl;		// 此处psome[0]也可以,提取psome指针指向的值  
	psome += 1;					// 将指针向下移动一个,注意:打印完psome[2]时,psome又加了一次
}
psome = psome - 3;				// 将指针移动回动态数组首位置,为下面释放内存做准备
delete [] psome;				// 释放psome指针指向的整个数组的内存空间(3~5行),且指针psome需指向数组首位置

注:

  • 模板类vector:
    • 语句:存储在堆中,头文件#include ,声明vector vint(4);,赋值cin >> vint.at(0);
    • 特点:动态调整数组及每个元素长度
    • 缺点:效率较低,有越界风险(用at语句)
  • 模板类array:
    • 语句:存储在栈中,头文件#include ,声明array arrint;,赋值arrint.at(0);
    • 特点:动态调整数组及每个元素长度
    • 缺点:有越界风险(用at语句)

2. 字符串

  • 赋值

    方法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);			
    pchar_1[5] = '\0';					// 第5位用\0补齐
    
    cout << pchar << endl;				// 打印字符串:指针pchar在cout语句中解释成字符串的值
    
    cout << (int*) pchar_0 << endl;		// 打印字符串首地址:指针指向的字符串的首地址
    

    3. 动态结构

  • 定义结构体

    struct sth							// 定义结构模板sth
    {
    	char name[20];
    	int volumn;
    	double price;
    };
    
  • 声明结构实例

    sth stlist = {};					// 常规初始化结构(栈)
    sth * plist = new sth;				// 堆中创建内存区块,并将plist指向这个区块首地址
    
  • 输入

    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++启蒙笔记(2)—数据类型
下一篇:C++启蒙笔记(4)—函数

你可能感兴趣的:(#,C++)