C++指针定义和使用

2023-10-26

目录:

1、指针简介

2、指针的声明和使用

1、指针简介

学习指针前需要先分清几个概念。

1.1内存单元的地址和内存单元的内容

​ 在程序中定义一个变量,当程序进行编译时就会给定义的变量分配内存单元,这个内存单元的大小由变量的数据类型决定。例如对有符号整型变量分配 4 个字节、对双精度浮点型变量分配 8 个字节等。在内存空间中每一个字节都有一个编号,这就是我们所说的地址,在地址所标识的内存空间中存放数据就相当于旅馆中相应的门牌号住相应的旅客一样。

​ 如程序定义一个有符号整型变量 a,假设编译时系统给变量 a分配的内存空间地址为 2000、2001、2002、2003 这四个字节。定义一个有符号整型变量 b,假设编译时系统给变量 b 分配的内存空间地址为 2004、2005、2006、2007 这四个字节。程序在经过编译以后将变量名转换为变量的地址,所以对于变量值的存取具是通过地址进行的。存储变量的内存空间的首地址称为该变量的地址。可由如下程序输出来看看a、b的值和地址。

例1.1:

#include <iostream>
using namespace std;
int main(void) {
int a,b;
cin>>a>>b;
cout<<"a="<<a<<'\n'<<&a<<endl;
cout<<"b="<<b<<'\n'<<&b<<endl;
return 0;
}	

1.2直接访问和间接访问

​ 例1代码在程序编译时执行过程是这样的:首先根据变量名与地址的对应关系,找到变量名a对应的地址 2000、2001、2002、2003,然后将从键盘输入的数据值传送到地址 2000 开始的有符号整型内存存储单元之中。找到变量名 b 对应的地址 2004、2005、2006、2007,然后将键盘输入的数据值传送到地址 2004 开始的有符号整型内存存储单元之中。假设为 a 输入 10,为b 输入 20,则此时在 a 表示的存储单元中的数据为 10,b 存储单元中为 20。当进行输出时,根据变量名 a找到变量的存储地址 2000、2001、2002、2003,然后在2000开始的四个字节中取出数据,即取出变量的值 10,然后将这个值输出。根据变量名 b找到变量的存储地址 2004、2005、2006、2007,然后在 2004 开始的四个字节中取出数据 20,然后输出。

直接访问:程序执行时,按变量名所对应内存地址处理相应的数据,这种按变量的地址直接存取变量的方法称为“直接访问”方式。
间接访问:如果将一个变量的地址放在另一个变量中,则存放地址的变量称为指针型变量,简称指针变量。这时存取变量,也可以间接地由指针变量取得该变量的地址进行,称为“间接访问”方式。

1.3什么是指针

理清上面几个概念后,我们再理解指针就会容易很多。

指针是指向某种数据类型对象的复合数据类型,提供了对所指向对象的间接访问,其保存的是另一个对象的地址。指针是 C++中的重要概念,它使得 C++可以在运行时取得对象的地址,并且可以通过这些地址操纵对象。正确运用指针可以有效地表示复杂的数据结构,用于数组和函数的控制,还能够动态分配内存且方便地使用字符串。掌握指针的使用可以使程序更加简洁和高效,也可以很好地提升性能。当然指针也是一把双刃剑,正确使用可以使程序提高效率,而错误使用会使程序崩溃。为了更好地驾驭指针,我们有必要充分了解 C++中指针的原理和用法,以便高效正确地使用指针。

2.指针的声明和使用

2.1指针变量的定义

注意:地址是内存单元的编号。指针变量就是存放内存地址的变量。指针和指针变量是两个不同的概念,但通常我们叙述时会把指针变量简称为指针。

指针变量定义的一般格式为:

数据类型 *变量名1,*变量名2,……,*变量名n;

在这里,“*”是一个标志,表示所定义的变量是一个指针变量,以表示与一般变量的区别,定义时星号的位置居左、居中、居右都是正确的。“&”为取地址运算符,这个运算符作用在一个对象上,返回的是该对象的存储地址。

int a=1;
float b=3;
char c='c';
int *p,*q;	//定义两个整型指针变量p,q。int*不是一种数据类型
float *s;	//定义一个实型指针变量s
char *r=&c; //这里表示将字符变量c的地址赋给字符指针变量r,也称r指向c,r不可指向a或b。

注意:

(1)指针变量的类型与其所指向的数据的类型必须相同。

(2)指针名是p,不是*p。

(3)定义时,每个指针变量前都要有星号。

2.2指针变量初始化

指针变量可以在定义时进行初始化,也可以在定义后进行赋值,赋值或者初始化的值必须是同类型的对象地址。

使用前一定要初始化!

int a=10;
int b=10;
int *p1=&a; //在指针变量定义时进行初始化
int *p2;	//首先定义指针变量
p2=&b;		//再进行初始化

指针变量也可以使用已经定义并初始化过后的指针变量进行初始化,如:

int a=10;
int*p1=&a;	//p1指向a
int*p2;
p2=p1;//p2=&a		//p2也指向a
cout<<"*p1="<<*p1<<endl;
cout<<"*p2="<<*p2<<endl;

C++中还提供了一种特殊的指针变量类型 void*,它可以保存任何类型对象的地址。如:

#include <iostream>
using namespace std;
int main(void) {
	int a=10;
	float b=10;
	int *p1=&a;
	void *p2=&a,*p3;		//可以保存任何类型对象的地址
	p3=&b;
	p3=&a;
	return 0;
}	

如果将一个指针变量的地址再放在另一个变量中,则该变量称为二级指针变量。二级指针变量定义的一般格式为:

数据类型  **变量名1, **变量名2, …, **变量名n;

在这里,“*”号同样起到标志的作用。如:

 char  a=’5’;
 char  *r=&a;          //r为一级指针
 char  **q=&r; //定义二级指针变量q,指向指针变量r

2.3指针变量的使用

指针变量(指针)能参与赋值运算、部分算术运算、关系运算和逻辑运算。

2.3.1赋值运算

*运算符为指针运算符(也称间接访问运算符,解引用运算符),其作用是求指针变量所指内存空间的值。

对指针本身(p)赋值运算是改变指针所指的位置;
*对指针所指对象(p)赋值运算是改变指针所指内存空间的内容。

例2.1:

#include <iostream>
using namespace std;
int main(void) {
int a[5]={1,2,3,4,5},*p1,*p2; 
p1=a,p2=p1;	             // 指针赋值, p1、p2 均指向a[0]
cout<<"*p1="<<*p1<<endl;
cout<<"*p2="<<*p2<<endl;
cout<<"a[0]="<<a[0]<<endl;
*p2=10;		// 指针所指对象赋值,a[0]的值赋成10
cout<<"----------"<<endl<<"*p1="<<*p1<<endl;
cout<<"*p2="<<*p2<<endl;
cout<<"a[0]="<<a[0]<<endl;
return 0;
}

除NULL(0)外,不能将一个具体的值赋给指针变量:允许将数值 0 或者在编译时可以获得 0 值的 const 变量赋值给指针变量.

悬空的指针,或所指内存空间不允许修改的指针,不能对其所指内存空间赋值;

当将指针变量初始化为0值常量表达式时,表明该指针变量不指向任何对象,指针悬空。

int *p1=0;//指针p悬空
int a=0;
int *p2;
p2=a;   //用法错误
const int b=0;
int *p3=b;//用法正确

对指针所指内存空间赋值时必须明确所指对象。

int *p1,*p2;
p1=&10;      //错误用法,&表示取地址,不能对字面值进行取地址操作
*p2=10;		 //错误用法

指针变量只能初始化或者赋值为同类型的变量地址或者另外一个指针变量.

int a;
int *p1=&a;
int p2=p1;   		//正确
int *p3;
p3=p1;		 		//正确
double *p4=p1;		//错误
double *p5;
p5=p1;				//错误
2.3.2算术运算

指针所指内存空间的算术运算等同于变量的算术运算;
指针本身的算术运算在连续的数组空间才有意义,且不宜超出数组的范围;
指针加/减上一个整数,表示其后/前的整数个存储单元的地址:
元素指针:其后/前若干个元素的地址;
行指针:其后/前若干行的行地址。
两个指针相减,表示相隔多少个存储单元.

#include <iostream>
using namespace std;
int main(void) {
	int a[5]={1,3,5,7,9},*p1=&a[3],*p2,*p3;            //p1 指向a[3]
	p2=p1+1,p3=p1-2;                       // p2 指向a[4],p3 指向a[1]
	cout<<"*p1="<<*p1<<'\t'<<"*p2="<<*p2<<'\t'<<"*p3="<<*p3<<endl;
	p2--,p3++;                                     // p2 指向a[3],p3 指向a[2]
	cout<<"*p2="<<*p2<<'\t'<<"*p3="<<*p3<<'\t'<<endl;
	*p3++=*p2++;           // a[2]赋值为7,p2 指向a[4],p3 指向a[3]
	cout<<"*p2="<<*p2<<'\t'<<"*p3="<<*p3<<endl;
	++*p1=++*p2; //a[3]自增为8,a[4]自增为10 后,将10 赋给a[3],p1(a[3]),p2(a[4])的指向不改变
	cout<<"*p1="<<*p1<<'\t'<<"*p2="<<*p2<<endl;
	int n=p2-p1,m=p3-p2;                      // n 的值为4-3=1,m 的值为3-4=-1
	cout<<"n="<<n<<'\t'<<"m="<<m<<endl;
	return 0;
}
2.3.3关系运算

指针变量可以参加所有的关系运算,用于判断指针所指的位置关系。
当位置关系成立时,运算结果为逻辑值真(true 或1);
当位置关系不成立时,其结果为逻辑值假(false 或0)。
如:

int a[5]={1,3,5,7,9},*p1,*p2,*p3;
p1=p2=a,p3=a+2;
//p1==p2、p1>=p2、p1<=p2、p1<p3、p3>p2 的值为真;
//p1!=p2、p1>p2、p1<p2、p1>=p3、p3<p2的值为假。
2.3.4逻辑运算

指针变量可以参加所有的逻辑运算。
当指针悬空时,即值为NULL(0)时,相当于逻辑值假;
当指针不悬空时,相当于逻辑值真。
如:

int a[5]={1,3,5,7,9},*p1=a,*p2=0,*p3;
static char *p4;
//p1、!p2、p1||p2、p3、!p4的值为真;
//p2、p1&&p2、!p3、p4的值为假。

对于&运算符和**运算符我们还有以下几点需要说明。*

(1)&*p 的含义

&和 * 这两个运算符的优先级是相同的,而两者的结合性是从右向左的,所以首先进行 * p的运算,再执行&的运算。而 * p相当于其指向的对象 a,所以&*pointer 与&a的作用是相同的。

(2)* &a的含义

首先进行&a 的运算,得到 a 的地址,再进行 * 运算,得到&a 所指向的变量即 a,所以*&a和a是等价的。

例:按照大小顺序输出两个数。

#include<iostream>
#include<string>
using namespace std;
int main()
{
    int a,b;
    int *p,*p1,*p2;
    cout<<"Put in a:";
    cin>>a;
    cout<<endl;
    cout<<"Put in b:";
    cin>>b;
    cout<<endl;
    p1=&a;
    p2=&b;
    cout<<"*p1="<<*p1<<endl;
    cout<<"*p2="<<*p2<<endl;
    if(a<b){    //指针进行交换
        p=p1;
        p1=p2;
        p2=p;        
    }
    cout<<"-------"<<endl;
    cout<<"*p1="<<*p1<<endl;
    cout<<"*p2="<<*p2<<endl;
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl; 
    return 0;
}

要注意上面的程序中a和b的值并没有进行互换,但是p1和p2的值进行了互换,它们互换后所指向的对象分别为b和a,所以输出*p1, *p2时,输出的是b和a的值。

参考:《从零开始学C++》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++指针定义和使用 的相关文章

  • 如何知道并加载特定文件夹中的所有图像?

    我有一个应用程序 C Builder 6 0 需要知道特定文件夹中的图像总数 然后我必须加载它们 在 ImageList 或 ComboBoxEx 中 或任何其他控件中 我怎样才能做到这一点 我知道如何在控件中加载图像 或保存在 TList
  • boost线程在中断时不打印退出消息

    我有这段代码用于执行三个线程 其中第二个线程应在按 Enter 时中断并打印退出消息 void input val DO STUFF return void process val DO STUFF try cout lt lt waiti
  • 我应该在单元测试中使用 AutoMapper 吗?

    我正在为 ASP NET MVC 控制器方法编写单元测试 这些控制器依赖于IMapper 我创建的用于抽象 AutoMapper 的接口 使用 Castle Windsor 通过构造函数注入传入 动作方法使用IMapper从领域对象映射到
  • 对数字进行向上和向下舍入 C++

    我试图让我的程序分别向上和向下舍入数字 例如 如果数字是3 6 我的程序应该四舍五入最接近的数字 4 如果该数字是3 4 它将向下舍入为 3 我尝试使用ceil库获取 3 个项目的平均值 results ceil marks1 marks2
  • 使用 VSTO 更改 Outlook 设置

    我刚刚花了大约 4 个小时试图弄清楚如何以编程方式检索 设置 Microsoft Outlook 2010 的 Outlook 设置 我所说的 设置 是指文件 选项 邮件下的设置 我想做的是检索用户设置的设置列表 自动化我们每天需要在某些消
  • C++ 在 Vector 中使用不可分配的对象

    我想将对象列表存储在std vector 但对象包含引用且无法分配给 但是 我可以复制构造该对象 我能想到的唯一选择是使用指针来包装对象并在需要分配指针时重新设置指针 但这样做的语法会显着降低可读性 特别是在使用迭代器时 我更喜欢另一种选择
  • C中有const吗?

    这个问题可能很幼稚 但是 有没有constC 中的关键字 从哪个版本开始 之间有任何语义和 或句法差异吗const在 C 和 C 中 C 和 C 之间在语法上没有差异const关键字 除了一个相当晦涩的关键字 在 C 中 自 C99 起 您
  • glDrawElements 只绘制半个四边形

    这是我的功能 void Object draw2 if mIsInitialised return Tell OpenGL about our vertex and normal data glEnableClientState GL VE
  • 成员初始值设定项列表中的求值顺序是什么?

    我有一个带有一些参数的构造函数 我假设它们是按照列出的顺序初始化的 但在一种情况下 它们似乎是按相反的顺序初始化的 导致中止 当我反转参数时 程序停止中止 下面是我正在使用的语法的示例 a 之前需要初始化b 在这种情况下 你能保证这个初始化
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • CMake - 将预构建库链接到 C# 项目

    我正在使用 CMake 构建 C 库 该库依赖于已构建的库 dll 我似乎无法让图书馆链接到我的图书馆 我尝试过使用target link libraries mylib external lib 我也尝试过暴力破解 reference e
  • _mm_max_ss 在 clang 和 gcc 之间有不同的行为

    我正在尝试使用 clang 和 gcc 交叉编译一个项目 但在使用时发现一些奇怪的差异 mm max ss e g m128 a mm set ss std numeric limits
  • 在可观察项目生成时对其进行处理

    我有一个IObservable它会生成一次性物品 并且在其生命周期内可能会生成无限数量的物品 因此 我想在每次生成新项目时处理最后一个项目 因此Using http reactivex io documentation operators
  • 使用 Linq 进行异步Where过滤

    我有一个List通过填充的元素async调用 WebService 没问题 我需要过滤该列表以便在应用程序视图上显示某些内容 我试过这个 List
  • 使用 AutoMapper 进行 LINQ GroupBy 聚合

    试图让查询工作 但老实说不确定如何 或者是否可能 进行它 因为我尝试过的一切都不起作用 共查询6个表 Person PersonVote PersonCategory Category City FirstAdminDivision Per
  • 通过 MSBuild 调用 cl.exe 时无限期挂起

    我正在尝试在我的 主要是 C 项目上运行 MSBuild 想象一下一个非常庞大的代码库 Visual Studio 2015 是有问题的工具集 Windows 7 SP1 和 VS 2015 更新 2 即使使用 m 1 从而迫使它仅使用一个
  • 多个同名内存数据库

    关系到这个答案 https stackoverflow com a 48446491 596758 我试图通过设置让多个上下文工作UseInMemoryDatabase以同名 下面的测试失败 第二个上下文为空 我还需要做什么才能在内存数据库
  • 局部静态变量初始化是线程安全的[重复]

    这个问题在这里已经有答案了 假设我有一个包含三个静态函数的类 如下所示 include
  • C++0x 中的新 unicode 字符

    我正在构建一个 API 它允许我获取各种编码的字符串 包括 utf8 utf16 utf32 和 wchar t 根据操作系统 可能是 utf32 或 utf16 新的 C 标准引入了新类型char16 t and char32 t没有这么
  • 在 C# 中读取/写入命令行程序

    我正在尝试与 C 的命令行程序进行对话 它是一个情绪分析器 它的工作原理如下 CMD gt java jar analyser jar gt Starting analyser 这是我想从我的 C 程序插入内容的地方 例如 I love y

随机推荐

  • 【iOS】属性关键字

    文章目录 前言 一 深拷贝与浅拷贝 1 OC的拷贝方式有哪些 2 OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝 3 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝 4 判断当前的深拷贝的类型
  • 论文阅读技巧之三遍法

    本文介绍了三遍法及其在文献调查中的应用 关键的思想是 你应该以三遍的时间来阅读论文 而不是从一开始就一直读到最后 每个pass都实现了特定的目标 并建立在前一个遍的基础上 第一个pass让您对本文有一个大致的了解 第二步让你掌握论文的内容
  • es查询对应索引下的数据结果

    执行语句 GET test index mapping pretty test index 索引名称 mapping 查询索引的结构 pretty 是参数 意思是格式化数据
  • spring手动开启事务,手动提交事务,手动回滚事务

    1 未加事务注解 或者事务配置 所以需要手动开启事务和手动提交事务和手动回滚事务 Autowired private PlatformTransactionManager txManager Autowired private ShopGr
  • Vue中表单手机号验证与手机号归属地查询

    下面是一篇关于Vue中如何进行表单手机号验证与手机号归属地查询的Markdown格式的文章 包含代码示例 Vue中表单手机号验证与手机号归属地查询 手机号验证和归属地查询是许多Web应用程序中常见的功能之一 在Vue js中 我们可以轻松地
  • 安装npm和cnpm

    一 简介 npm是nodejs的包管理工具 用于node插件管理 cnpm是淘宝在中国做的nodejs镜像 避免访问国外的nodejs网站出现异常 二 安装nodejs 1 安装 有两种选择一种是安装文件安装 一种是免安装的zip包 这里我
  • 使用Rust写操作系统(1)-安装rust开发环境

    安装cargo及rust编译环境 sudo curl https sh rustup rs sSf sh 如图 选择自定义安装 在版本选择的时候 一定要选择nightly 因为开发操作系统要使用到一些非稳定版本的功能 选择完成后 继续安装即
  • Java序列化详解

    序列化是一种将对象转换成字节流的过程 以便在网络上传输或将其保存到磁盘上 Java提供了一种称为Java序列化的机制 它可以将Java对象转换成字节流 并在需要时将其还原回对象 在本文中 我们将介绍Java序列化的使用方法 并提供一些示例代
  • 都2021了作为一名Android开发者,还不学音视频开发?我劝你早点认清现实!

    缘起 最近经常遇到一些同学问我如何学习音视频 怎样才能快速上手 还有一些对音视频不了解的同学问我该不该学习音视频 作为一名音视频行业的10年Android老兵 我有一些思考分享给大家 希望能对你有所帮助 大趋势 从未来的大趋势来看 随着5G
  • 【C语言位运算符及原码输出】

    C语言位运算符及原码输出 原码 补码 反码基础概念 按位与 按位或 按位异或 按位与 lt lt 按位左移 gt gt 按位右移 位运算符注意事项 两个操作数均以补码参与计算 得到的结果为补码 需将结果转为原码才是最终答案 原码 补码 反码
  • element-plus 提示找不到名称“ElMessage”。ts(2304)

    文章目录 1 安装element plus 2 main ts 引入ElMessage 3 vite config ts 中配置 4 在vscode中使用会报错 找不到名称 ElMessage ts 2304 1 安装element plu
  • umi使用mock

    引入 Mock import Request Response from umijs deps compiled express import Mock from mockjs 定义数据类型 export default GET api t
  • 微信小程序vant组件库安装

    vant组件库安装步骤 1 通过npm安装 在微信开发者工具目录空白处右击 在外部终端窗口中打开或直接在文件路径中输入cmd回车 2 安装之前初始化npm包 再安装 npm init y 通过 npm 安装 npm i vant weapp
  • Ubuntu下如何将普通用户提升到root权限

    1 打开超级终端 输入指令sudo gedit etc passwd 2 则找到crystal x 1000 1000 crystal home linuxidc bin bash 将两个1000改成0即可 3 重新登陆之后打开超级终端发现
  • BLEU 评价指标总结

    Bleu 评测 一 Bleu通常用来度量一组机器产生的翻译句子集合 candidates 与一组人工翻译句子集合 references 的相似程度 Bleu的具体计算过程看下图 在这里解释一下 式中的n 为当前匹配n gram的长度 这里的
  • Win10 + vs2017 编译并配置tesseract4.1.0

    tesseract 是一个开源的OCR Optical Character Recognition 光学字符识别 引擎 本文就介绍一下自己在编译 tesseract4 1 0时遇到的一些坑 希望能给大家带来一些帮助 一 下载 tessera
  • mybatis mysql autoreconnect=true_Mysql8.0主从搭建,shardingsphere+springboot+mybatis读写分离...

    cd usr local mysql mkdir mysql files chown mysql mysql mysql files chmod 750 mysql files bin mysqld initialize user mysq
  • StringUtils中 isNotEmpty 和isNotBlank的区别 以及StringUtil类的方法

    StringUtils方法的操作对象是java lang String类型的对象 是JDK提供的String类型操作方法的补充 并且是null安全的 即如果输入参数String为null则不会抛出NullPointerException 而
  • Apollo:实时通信架构CyberRT入门

    发现一开始就深入源码 很容易陷进去 特别是模块非常多的情况 需要看很多遍才能理解清楚 要写出更容易理解的文档 需要的不是事无巨细的分析代码 更主要的是能够把复杂的东西抽象出来 变为简单的东西 一个很简答的例子是画函数调用流程图很简单 但是要
  • C++指针定义和使用

    目录 1 指针简介 2 指针的声明和使用 1 指针简介 学习指针前需要先分清几个概念 1 1内存单元的地址和内存单元的内容 在程序中定义一个变量 当程序进行编译时就会给定义的变量分配内存单元 这个内存单元的大小由变量的数据类型决定 例如对有