C++学习 指针

前言

C++语言之所以强大和灵活,很大一部分体现在其灵活的指针运用上。指针是C++语言的灵魂。但是指针的灵活性也导致了其难以控制,有很多bug也是基于指针而产生的。

首先来说明变量的地址:变量是内存变量的简称,在C++中,每定义一个变量,系统就会给变量分配一块内存,内存是有地址的。(我们将内存分为小小的内存单元,每个内存单元为一个字节,又为每个字节规定一个编号,这个编号就是地址)

指针的基本概念

指针是一个变量,一个特殊变量,用来存放一个变量在内存中的起始地址。在C++中,每定义一个变量,系统会分配一块内存

对于指针我们关心这四个概念:指针类型、指针所指向变量的类型、指针的值或者叫做所指向的内存区,指针本身所占用的内存区。

指针的定义语法:数据类型 *变量名;
其中数据类型指的是指针变量所指向的变量的数据类型,*表示这是一个指针变量,变量名表示的是指针变量的名字。

例子:int *p1;//表示p1是一个指针变量,它的值是某个整型变量的地址

指针类型
指针也是变量,是变量就会占用内存空间。指针变量的类型分为整形指针、字符指针、数组指针等等。在64位的操作系统中,不管什么类型的指针,占用的内存都是8字节。32位是4个字节。

由于指针是一种独立的变量类型,它存储的值是内存地址,所以指针也是有类型的,指针的类型是指指针本身的类型。从语法角度看,把指针声明语句中的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。

int *ptr; //指针的类型是int *  
char *ptr; //指针的类型是char *  
int **ptr; //指针的类型是 int ** 
int (*ptr)[3]; //指针的类型是 int(*)[3]  
int *(*ptr)[4]; //指针的类型是 int *(*)[4]

划分指针类型的原因:

  1. 指针的类型决定了指针在被解引用时所访问的权限。
  2. 指针的类型决定了指针向前或者向后退一步走的“距离”

指针所指向的类型
知道指针所指向的数据类型的作用:有一个变量在内存中,系统不会记录在内存中的开始位置和结束位置,在程序中变量的地址是变量在内存中的起始位置,再知道变量占用的内存空间就可以准确定位变量的位置,总的来说数据类型决定了占用内存的大小,决定了系统如何操作数据。

从语法上看,你只需把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:

int *ptr; //指针所指向的类型是int  
char *ptr; //指针所指向的的类型是char 
int **ptr; //指针所指向的的类型是 int *  
int (*ptr)[3]; //指针所指向的的类型是 int()[3]  
int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]

指针的值
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。声明指针后,指针所指向的类型已经有了,但如果指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

在这里要记住,指针的值就是指针的所指向的内存地址的首地址,而指针的指向则是以XX为首地址的一片内存区域

指针本身所占用的内存

在64位的操作系统中,不管什么类型的指针,占用的内存都是8字节。32位是4个字节。

不管什么类型的变量,它们的地址都是一个十六进制的数。

指针的使用

声明指针变量之后,在没有赋值之前,里面是乱七八糟的值,不能使用指针。

指针存放变量的地址,因此指针名表示的是地址。

指针使用的语法:指针名=&变量名 //&表示的是取地址

例子:

	int* p;
	int a = 3;
	p = &a;
	cout << "p=" << p << endl;

指针用于函数的参数

指针用于函数的参数即把变量地址作为参数传给函数。

把变量的地址作为参数传给函数的优点是:

  1. 在函数中可以通过解引用方式修改实参的值。*运算符被称为解引用运算符
  2. 减少内存的拷贝,提高效率。

解引用:*p //p是指针名

值传递:在函数中修改形参值,不能改变实参。

用const修饰指针

用const修饰指针有三种:

  1. 常量指针const 数据类型 *变量名
    常量指针不能通过解引用方法修改内存地址中的值(但可通过原始变量名修改),常量指针指向的变量可改变。

  2. 指针常量数据类型 const *变量名
    指针常量要赋初值,其指向对象不可改变。一般用于修饰函数形参,表示希望在函数只修改内存地址中的值。

  3. 常指针常量const 数据类型 const *变量名
    常指针常量即不能通过解引用的方法修改内存地址中的值,其指向对象也不可改变。

**注:**如果函数的形参的值不需要改变建议加上const让别人明白。

void关键字

void表示无类型。void关键字有三种用途

  1. 函数返回值用void表示函数没有返回值。
  2. 函数参数填void表示函数不需要输入参数(括号中什么不填也可表示此意义)。
  3. 函数形参用void*,表示接受任意数据类型的指针。
    **注:**不能用void声明变量。

C++内存模型

在这里插入图片描述
C++内存分配从代码段到内核空间是从低到高的。

栈区:内存分配是向下减小的。

堆区:内存分配是向上增大的。

栈和堆的区别:

  1. 管理方式不同:栈是编译器自动管理的,堆需要手动释放内存。
  2. 空间大小不同:栈很小,一般只有8M,堆的内存空间比栈大,受限于物理内存空间。

动态分配内存new和delete

若需要处理大量的数据,必须使用堆区内存。

使用堆区内存的四个步骤:

  1. 声明一个指针。
  2. 用new运算符向内存申请一块内存,让指针指向这块内存。
  3. 通过对指针解引用的方法,像使用变量一样使用这块内存。
  4. 如果这块内存不用了,用delete运算符释放这块内存。

申请内存的语法:new 数据类型(初始值);
释放内存的语法:delete 地址

注:

  1. 动态分配出来的内存,没有变量名,只能通过指向它的指针来操作内存中的数据。
  2. 动态分配的内存不用了,必须用delete释放它,否则有可能用尽系统的内存。

二级指针

用于存放指针变量的地址。

空指针

在C和C++中,用0或NULL都可以表示空指针。

声明指针后,在赋值之前,让它指向空,表示没有指向任何地址。

在函数中,应该有判断形参是否为空指针的代码,目的是保证程序的健壮性。

用0和NULL表示空指针会产生歧义,C++11建议用nullptr表示空指针,也就是(void*)0。

野指针

所谓野指针即指针指向的不是一个有效合法的地址。

访问野指针可能会导致程序崩溃。

造成野指针的三种情况:

  1. 指针在定义的时候,如果没有初始化,它的值是不确定的。
  2. 指针指向了动态分配的内存,内存被释放后,指针不会置空,但指向的地址已经失效。
  3. 指针指向的变量已超过变量的作用域。

规避方法:

  1. 指针在定义的时候,如果没有地方指就初始化为nullptr。
  2. 动态分配的内存被释放后,将其置为nullptr。
  3. 函数不要返回局部变量的地址。

函数指针和回调函数

函数指针主要用于函数的回调。

使用函数指针的三个步骤:

  1. 声明函数指针
  2. 让函数指针指向函数的地址
  3. 通过函数指针调用函数

函数指针的声明语法:函数类型(*函数指针名)(函数形参表); //函数类型是指返回值和参数列表
例子:int (*p)(int,string);

#include <iostream>  //包含头文件
using namespace std;

void func(int no, string str)
{
	cout << "亲爱的" << no << "号:" << str << endl;
}

int main()
{
	int bh = 3;
	string message = "我是一只啥啥鸟";

	func(bh, message);

	void (*pfunc)(int, string); //声明函数指针
	pfunc = func;
	pfunc(bh, message);//通过函数指针调用函数
}

在C++中,函数名就是函数的地址
回调函数是把一个函数的代码嵌入到另一个函数中,调用者函数提供了主体的流程和框架,具体功能可以由回调函数实现。

给回调函数传递参数有两种方法:

  1. 由调用者函数提供实参
  2. 把实参从外面传进去

回调函数在多线程和网络通讯中很常用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值