C++杂记02 指针

news/2024/12/26 13:33:36/文章来源:https://www.cnblogs.com/sirenxie/p/18632640

好久没有更新推文了,最近换了工作,时间相对多了一点,有一点时间把过去的一些笔记内容给整理一下。能坚持学习和整理是一件很难的事情,当下大多数人的生活都相当碎片化,很多事做着做着就中断了,希望我能把我学习C++和OpenFOAM的一些内容写完。

指针在OpenFOAM里面是一个很常见的内容,例如使用auto关键字,智能指针。指针本质上就是C/C++中提供一种直接访问内存数据的一种方式,效率高,但是用法复杂一些。很多时候,需要指针做的事情,使用引用也可以,尽量避免指针的使用,因为指针使用不当,容易造成内存泄漏,造成程序错误和崩溃。

指针的声明和使用

指针的声明很简单,一般是类型名*。和引用方式不同,指针是可以使用空指针进行初始化的,而不是需要指向一个已经存在的变量的内存地址。

假设现在有一个指针类型p,并且p并不是空指针。*p就是解引用操作,可以访问p所对应的内存地址中的变量内容,并且在没有const关键字限定的情况下,可以对变量值进行修改。

#include<iostream>
using namespace std;
int main()
{int a = 10;int* p11 = NULL;//C++11之前的空指针int* p2q(nullptr); //现代C++空指针声明方式,都可以使用p11 = &a; //这里&号取地址符,获取变量的内存地址p2q = &a; //可以多个指针指向一个内存地址,指向哪个地址就可以通过指针访问内存数据cout<<"p11的地址为:"<<p11<<"(解引用)其值为:"<<*p11<<endl;// *运算符搭配类型名时,可以声明指针// 搭配指针时,为间接寻址运算符,可以访问内存的值,这一操作为解引用cout<<"p2q的地址为:"<<p11<<"(解引用)其值为:"<<*p2q<<endl;//如果需要限制指针的指向,可以搭配const关键词使用//同样可以对指针访问和内存变量的值*p11 = 100;cout<<"变量a的值为:"<<a<<endl;//搭配输入输出操作cin>>*p11; //输入值200cout<<"变量a的值为:"<<a<<endl;return 0;
}

const限定

使用const关键字可以对指针的指向,内存数据是否可修改做出限定。基本上有四种情况:

  1. 非常量指针指向非常量数据

    这种情况就是没有const关键字修饰,如上面的代码所示。指针可以被修改指向别的变量,并且可以通过这个指针修改内存变量的值。

  2. 非常量指针指向常量数据

    通过const + 类型名* + 指针名进行定义,指向某一个内存变量。但是不能够通过该指针修改内存变量的值。不过可以将该指针指向其他内存变量。一般在函数中,或者类中引用其他类/对象时,出于保护被使用对象的目的,不改变对象值。

  3. 指向非常量数据的常量指针

    通过类型名* + const + 指针名进行定义,该指针只能指向一个内存变量,且不能被修改指向其他内存变量。不过可以通过该指针修改内存变量值。这种指针就是对指针的指向范围进行了限制,将指针绑定在某一个内存变量上,成为该变量的“专属指针”。当然一个内存变量可以有多个专属指针。

  4. 常量指针指向常量数据

    该指针就是前面两种指针的结合体,通过const + 类型名* + const进行限定,该指针既不能被修改指向其他内存变量也不能够修改内存变量的值。

#include<iostream>
#include<array>
using namespace std;
int main()
{const int value[] = {11,12,13,14,15};std::array arrayint(std::to_array(value));for(int i = 0;i<arrayint.size();i++){cout<<arrayint[i]<<endl;}//或者使用迭代器遍历cout<<"使用迭代器遍历array"<<endl;for(std::array<int,arrayint.size()>::iterator it = arrayint.begin();it != arrayint.end();it++){cout<<*it<<endl;}//auto关键词,让C++自己去推断变量的类型和大小//指向常量数据的非常量指针int a = 100;const int* p{&a};//*p = 200; //error, 不可以通过该类型指针修改变量数据,表达式是可被修改的左值cout<<"指针指向的内容是:"<<*p<<endl;int b = 200;p = &b; //不能修改变量值,但是可以更换指向别的变量cout<<"指针指向的内容是:"<<*p<<endl;//指向非常量数据的常量指针int* const p2 = &a; //该类型指针可以修改变量值,但是不能再指向其他变量cout<<"p2指针指向的内容是:"<<*p2<<endl;*p2 = 1000;cout<<"p2指针指向的内容是:"<<*p2<<endl;//p2 = &b; //不可以指向其他变量//指向常量数据的常量指针//只能读取数据,既不能修改变量值,也不能指向其他内存地址const int* const p3 = &a;cout<<"p3指针指向的内容是:"<<*p3<<endl;return 0;
}

程序的输出为:

11
12
13
14
15
使用迭代器遍历array
11
12
13
14
15
指针指向的内容是:100
指针指向的内容是:200
p2指针指向的内容是:100
p2指针指向的内容是:1000
p3指针指向的内容是:1000

指针的运算

在学C语言时候,就提到数组其实也是一种指针,之前的文章也说过,函数也有函数指针。数组的名字就是一种指针名。如果我们访问数组中的某一个数据,通常是数组名[索引]。在连续内存中,指针的加减并不是简单的对指针的地址加减相应的值,而是移动指针指向的内存地址。移动的距离为sizeof(内存变量类型)。

当对指针进行加法运算时,并不是简单地按照常规数值相加的方式来处理。实际上,指针加法的结果取决于指针所指向的数据类型的大小(字节数)。例如,如果指针指向 int 类型(通常 int 在常见系统中占 4 个字节),那么指针加 1,实际上是让指针在内存中的地址向后移动了 sizeof(int)(也就是 4 个字节)的距离,使其指向下一个 int 类型元素所在的内存位置。

和指针加法类似,指针减法也是基于所指数据类型的大小来操作的。指针减 1,就是让指针在内存中的地址向前移动对应数据类型大小的距离,使其指向前一个同类型元素所在的内存位置。另外,两个同类型的指针相减,可以得到它们之间相隔元素的个数(以对应的数据类型为单位)。例如,有两个指向 int 数组元素的指针 p1p2p1 - p2 的结果就是它们之间间隔了几个 int 类型的元素个数。

使用数组名作为函数参数传递的方法,现在已经不提倡了,因为安全性存在问题。给出数组名,给出数组第一个元素的内存地址,但是没有给出数组的长度。在实际使用时,会容易出现访问不存在的内存变量导致程序崩溃。

#include <iostream>
using namespace std;int main() {// 示例 1:指针加法遍历数组int arr[] = {1, 2, 3, 4, 5};int* ptr = arr;  // 让指针指向数组的首元素for (int i = 0; i < 5; ++i) {cout << *ptr << " ";  // 输出当前指针指向的元素值ptr = ptr + 1;  // 指针向后移动一个元素位置(地址增加 sizeof(int) 字节)}cout << endl;// 示例 2:指针减法计算间隔元素个数int arr2[] = {10, 20, 30, 40, 50};int* p1 = &arr2[2];  // 指向数组中第三个元素(索引为 2)int* p2 = &arr2[0];  // 指向数组的首元素int diff = p1 - p2;  // 计算两个指针之间间隔的元素个数cout << "两个指针之间间隔的元素个数为: " << diff << endl;return 0;
}																							

指针和引用的简单说明

指针指向内存变量的地址,可以直接修改内存变量的值。而引用,其特性一句话就是变量的“别名”。在函数中的体现尤为明显,下面为交换两个数的程序:

#include<iostream>
using namespace std;void swap(int a,int b)
{int c = a;a = b;b = c;
}// void swap(int& a,int& b) //使用引用的方式交换两个变量
// {
//     int c = a;
//     a = b;
//     b = c;
// }// void swap(int* a, int*b ) //以传值的形式传地址给形参a和b,实际上并不能起到交换数据的作用
// {
//     int *c = a;
//     a = b;
// }// void swap(int* a, int*b ) //通过内存变量的形式实现数据交换
// {
//     int c = *a;
//     *a = *b;
//     *b = c;
// }
int main()
{int m = 5,n=10;cout <<"交换数据前: "<<"m = "<<m<<" n = "<<n<<endl;swap(m,n);cout <<"交换数据后: "<<"m = "<<m<<" n = "<<n<<endl;return 0;
}

函数的传入参数为形参,本质是传值。在这段程序中,a和b只是拷贝了m和n的值。但是因为a和b只是临时变量,有作用域限制,只在swap函数中起作用,执行完函数并没有真正交换a和b的值。

交换数据前: m = 5 n = 10
交换数据后: m = 5 n = 10

而下面的函数,则是引用的用法,引用相当于函数的别名。程序中,a和b相当于是m和n的别名了。其实已经和m、n在内存中的绑定。a和b的修改是能够修改m和n的值。

void swap(int& a,int& b) //使用引用的方式交换两个变量
{int c = a;a = b;b = c;
}
交换数据前: m = 5 n = 10
交换数据后: m = 10 n = 5

下面的函数中,a和b只是拷贝了m和n的地址,如果不通过指针,访问内存中的数据并修改,其实也仅仅是拷贝内存变量的地址而已。a和b仍然是作用域局限在swap函数中的变量,当函数执行完毕,m和n的值也并不会交换。

void swap(int* a, int*b ) //以传值的形式传地址给形参a和b,实际上并不能起到交换数据的作用
{int *c = a;a = b;
}
交换数据前: m = 5 n = 10
交换数据后: m = 5 n = 10

如果我们使用间接寻址运算符,进行解引用操作,访问非常量数据并且进行修改,就可以实现数据的交换。

void swap(int* a, int*b ) //通过内存变量的形式实现数据交换
{int c = *a;*a = *b;*b = c;
}
交换数据前: m = 5 n = 10
交换数据后: m = 10 n = 5

在使用指针和引用时,还有左值和右值的概念和区分。这一块留待下次写智能指针的内容。

简单地说,变量名,是直接操作内存变量的方法,避免了通过内存地址去查找变量并且做读取和修改操作。引用是变量的别名。引用必须得有实际变量存在做初始化。指针则指向一个内存地址,通过指针寻址和解引用操作访问和修改变量值。

下面代码简单说明了一下指针和引用的关系:

#include<iostream>
using namespace std;int main()
{int m = 5,n=10;int& k = m;cout<<"m的值: "<<m<<" k的值为:"<<k<<endl; //k是m的引用int* p = &m; //需要注意&符号的用法,可以用来声明引用,也可以做取地址符号cout<<"m的值: "<<m<<" k的值为:"<<k<<" p指向的值为: "<<*p<<endl;*p = 100;cout<<"修改后m的值: "<<m<<" k的值为:"<<k<<" p指向的值为: "<<*p<<endl;int* q = &k; //这里对引用取地址,实际上仍然对m取地址cout<<"p指针地址为: "<<p<<" q指针地址为: "<<q<<endl;//引用不占据内存空间,而指针占用,推荐使用引用,现代C++对指针开始逐步淡化//有指向指针的引用,相当与指针的“别名”,从左往右看操作符int* &r = p;cout<<"p指针地址为: "<<p<<" q指针地址为: "<<q<<" r指针的地址为: "<<r<<endl;cout<<*p<<" "<<*q<<" "<<*r<<endl;//使用指针的引用改变值*r = 200;cout<<"修改后: "<<*p<<" "<<*q<<" "<<*r<<endl;//不存在指向引用的指针,因为引用并无实际内存地址,做变量别名return 0;
}
m的值: 5 k的值为:5
m的值: 5 k的值为:5 p指向的值为: 5
修改后m的值: 100 k的值为:100 p指向的值为: 100
p指针地址为: 0x7ffcc0673ee0 q指针地址为: 0x7ffcc0673ee0
p指针地址为: 0x7ffcc0673ee0 q指针地址为: 0x7ffcc0673ee0 r指针的地址为: 0x7ffcc0673ee0
100 100 100
修改后: 200 200 200

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/859277.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

龙哥量化:通达信文华技术指标-双均线策略叠加分时均线,量化策略思路详细分析

如果您需要代写技术指标公式, 请联系我。 龙哥QQ:591438821 龙哥微信:Long622889 也可以把您的通达信,文华技术指标改成TB交易开拓者、金字塔、文华8的自动交易量化策略 开始分享一些细致化的思路和写法,我常用的是TB交易开拓者。对量化感兴趣的朋友可以多交流 这篇介绍重…

MDS100-16-ASEMI电机专用整流模块MDS100-16

MDS100-16-ASEMI电机专用整流模块MDS100-16编辑:ll MDS100-16-ASEMI电机专用整流模块MDS100-16 型号:MDS100-16 品牌:ASEMI 封装:M18 正向电流:100A 反向电压:1600V 引脚数量:5 芯片个数:6 芯片尺寸:50MIL 漏电流:>10ua 恢复时间:>2000ns 浪涌电流:920A 芯片…

Nginx使用手册

由于格式和图片解析问题,为了更好的体验可前往 阅读原文Nginx(发音为 "engine-x")是一个高性能、开源的HTTP和反向代理服务器,也可以作为电子邮件(IMAP/POP3)代理服务器、以及通用的TCP/UDP代理服务器。它由俄罗斯的程序员Igor Sysoev创建于2002年,其目的是解…

在线性坐标系中绘制三角函数图象

本文记述了用 Matplotlib 在线性坐标系中绘制三角函数图象的例子。 代码主体内容如下: ...def main():fig, axs = plt.subplots(1, 3, figsize=(14,4.5)) #1axs[0] = configure_axes(axs[0], Trigonometric Function\t\t\t + r$sine$, 2*np.pi, 1, np.pi, np.pi/2…

美团后端暑期一面,本来收到感谢信,但又复活了!

今天来分享的是一位读者的美团暑期实习一面面经,主要是一些常规八股,难度还是有的,部分题目确实不太好回答。这位同学回答的不是很好,本来是收到感谢信了。结果,过几天又收到复活赛邀请,复活赛倒是打赢了,已oc。 1、线程池的参数/*** 用给定的初始参数创建一个新的Threa…

Makefile文件中,两个$的变量变量$$Xxx 与一个$的变量 $Xxx的区别

使用来引用 Makefile 中的变量。使用$$来引用 shell 中的变量,以确保在传递给 shell 时保留单个符号。原文地址:Makefile文件中,两个$的变量变量$$Xxx 与一个$的变量 $Xxx的区别Makefile 中的变量引用 在 Makefile 中,$ 符号用于变量替换,但它的使用方式有一些细微的区别:…

【日记】各位圣诞节快乐呀!(566 字)

正文不知道为什么最近总是做噩梦。昨天晚上梦到我一枪射死鱼儿,然后兄长用一瓶 4 块钱 1L 的冰红茶将我敲死,最后全人类死于小行星撞地球。有一颗小行星刚好降落在我家附近的山上,然后散射出了无数激光,把我家切割成一块一块的。也没塌,不知道哪个巫师用了魔法,把周围的房…

LDA主题模型——Python实现(三)

img { display: block; margin-left: auto; margin-right: auto } table { margin-left: auto; margin-right: auto } LDA假设每个文档都是多个主题的混合,每个主题又是多个词语的混合。它通过识别文档中的词语分布来推断出文档的主题结构。LDA的一个简单比喻是冰淇淋店:每个…

工具大全-dirsearch探测Web目录

目录扫描工具dirsearch非常详细使用方法dirsearch介绍dirsearch是一款开源的、基于Python开发的命令行工具,主要用于对Web服务器进行目录和文件的扫描,以发现潜在的安全漏洞。 dirsearch下载地址: https://github.com/maurosoria/dirsearch 运行环境:必须安装python3为什么…

Sealos Devbox 基础教程:使用 Cursor 从零开发一个 One API 替代品

随着技术的成熟和 AI 的崛起,很多原本需要团队协作才能完成的工作现在都可以通过自动化和智能化的方式完成。于是乎,单个开发者的能力得到了极大的提升 - 借助各种工具,一个人就可以完成开发、测试、运维等整条链路上的工作,渡劫飞升成为真正的 “全干工程师”。 之前我们分…

【详解】低功耗低成本的Open开发方案

Open开发应用需求,有没有低功耗、低成本方案? 今天我们一起来聊聊Air780EP模组。 一、Air780EP核心信息描述 运营商支持: Air780EP模组面向国内的全网通模组,支持移动、电信、联通三大运营商。 仅比Air700E系列相对大一些: Air700ECQ/Air700EAQ/Air700EMQ; 与Air780E系列…