C++学习笔记之指针(基础)

news2024/12/26 11:00:20

C++学习笔记之指针(基础)

https://www.runoob.com/cplusplus/cpp-pointers.html

C/C++中的指针内容是早就盛名在外了,当然了,想要随心所欲地使用也是要做很多功课的,先简单了解下吧~

首先,我们需要了解一点内存的概念:
每一个变量都有一个内存位置,而这个变量的内存位置会有地址,通过&可以进行访问
在这里插入图片描述

int a = 10;
cout << &a << endl;   // 访问变量地址

顺道再复习下数组

int a[10];
cout << &a << " " << &a[0] << endl;   // 数组地址取的就是首个元素的地址,二者含义相同

在这里插入图片描述
好,明白这点以后,下面进入正题

1、指针是什么?

指针是一个变量,它的值为另一个变量的地址(内存位置的直接地址)

由于它作为一个变量,因此在使用之前也需要进行声明

类型 *变量名
  • 类型:C++ 数据类型
  • *:表明该变量是指针
  • 变量名:合法的标识符
int *ip;   // int类型指针
double *ptr;   // double类型指针

:无论是int类型指针、double类型指针,还是其他各种类型的指针,作为指针变量的值,都是一样的,其实质都是内存地址,均为代表内存地址的长的十六进制数

各个类型指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同,也就是对应内存是存放了什么数据类型,这点是不同的

这就好比外卖员去送东西,外卖员去送外卖只关心顾客住在哪(内存地址),无论是屋子里住的是电影明星还是程序员(指向变量的类型),它只管按照地址把外卖送到就行了

2、使用指针

通常情况下,使用指针只需要三板斧
①定义一个指针变量
②把变量地址赋值给指针
③访问指针变量中可用地址的值

int value = 100;  
int *ptr = &value;   // 将变量地址给指针变量ptr
cout << *ptr << endl;  // 访问指针地址对应的值,使用*
cout << ptr << endl;  // 访问指针的值,为变量地址

在这里插入图片描述
在这里插入图片描述

*的作用为解引用,用于获取该地址对应的内容,即指针指向变量的值

3、空指针

如果指针变量声明的时候,没有确切的地址可以赋值,通常会为指针赋值NULL
被赋值NULL的指针称为空指针

NULL在标准库中定义为0

int *ptr = NULL;
cout << ptr << endl;    // 0

在这里插入图片描述
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的,表明该指针不指向一个可访问的内存位置

int *ptr = NULL;
cout << ptr << endl; 
if (ptr)   // 通常使用类似的方式判断指针可用
{
    // todo 
}

如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针,通常未初始化的变量存有一些垃圾值,导致程序难以调试

4、指针的算术运算

C++指针实际上是地址,因此可以对指针进行一些算术运算,包括++--+-

指针的算术运算根据指针的类型和大小决定移动的距离,比如如果是int类型的指针,int所占字节为4字节,即32位,那么指针每移动一个单位,便是4字节

  • 加法运算
    指针当前指向的地址的基础上加n个单位,每个单位由对应指针的类型决定
    在使用指针操作数组时,常常使用这些指针运算符,数组的类型统一,因而下一个元素正好可以方便地使用指针的加减来寻址
    int arr[] = {1, 2, 3, 5, 4};
    int *ptr = arr;    // 指向数组首地址
    cout << *ptr << endl;
    ptr ++;    // 数组下一个元素
    cout << *ptr << endl;
    
    但是需要注意,当使用指针操作时,要确保指针指向有效的内存区域,否则可能会导致未定义行为或程序崩溃
    在操作数组时,尤其要小心避免指针超出数组的范围
  • 减法运算
    与加法相反,在当前指针指向的地址的基础上减去n个单位
    int arr[] = {1, 2, 3, 5, 4};
    int *ptr = &arr[1];   // 指向数组第二个元素的地址
    cout << *ptr << endl;
    ptr --;   // 向前移一位元素
    cout << *ptr << endl;
    
  • 指针与指针间的减法
    两个指针间的元素个数
    int arr[] = {1, 2, 3, 5, 4};
    int *ptr1 = &arr[1];
    int *ptr2 = &arr[3];
    cout << ptr2 - ptr1 << endl;    // 相距2个元素
    
  • 指针与整数间的比较
    可以将指针与整数进行比较运算,常用于判断指针是否指向某个有效的内存位置
    int arr[] = {1, 2, 3, 5, 4};
    int *ptr1 = &arr[1];
    int *ptr2 = &arr[3];
    cout << (ptr1 == ptr2) << endl;  // 假,返回0
    cout << (ptr1 < ptr2) << endl;   // 真,返回1
    

需要注意,在进行关系比较时,指针需要属于同一数组,否则关系比较结果是未定义的
并且,在进行关系比较前,需要确保指针非空,否则也会产生未定义的行为

5、指针 vs 数组

在C++中,指针和数组是密切相关的,透过表象看本质,终究还是对内存地址的操作

数组是内存中连续的单元组合而成,因此其中每个元素是同样的类型,这就可以借助于指针在数组上进行运算,因为每一步正好就是一个类型长度,从数组首地址开始根据计算可以访问其中的各个元素

int arr[] = {1, 2, 3, 5, 4};
int *ptr = arr;
for (int i = 0; i < 5; i++)
{
    cout << *ptr << endl;
    ptr ++;
}

然而,两者之间是无法完全画等号的,如果把数组名看做指针,使用*更换内容,是可以的,但是使用++一类的运算来修改,是不行的,因为修改值不会涉及地址的变化,而使用++等运算符,数组的首地址就变了呀,那还不乱套了
在这里插入图片描述
看到报错提示,数组名是不可作为左值的,它的地址不可被改变
在这里插入图片描述
数组名属于指向数组开头的常量,因而不能重新对其修改,但是其中内容可以更改,也可以采用指针的方式进行访问

*(arr + 2)   // 相当于arr[2] 结果为3

6、指针数组

前面学习了数组,现在又了解了一些指针的内容,此时将二者结合一下,便有了指针数组的概念

其实这个比较好理解的,也就是数组的每个元素都是一个指针,指向某个元素的地址

int  arr[] = {1, 3, 9};
int *ptr[3];
 
for (int i = 0; i < 3; i++)
{
   ptr[i] = &arr[i]; // 取整数的地址
}
for (int i = 0; i < 3; i++)
{
   cout << *ptr[i] << endl;
}

在下面这个例子中,也是一个指针数组,看右边知道,每个元素是字符串,下意识反应字符串其实就是字符数组,数组在某种程度上相当于指针,所以意味着,我们可以把每个元素看做字符指针,那么就可以将字符串作为字符指针数组的元素了,这种转换意识还是需要的

const char *ptr[3] = {
 "Bob",
 "Bill",
 "Mike"
};
 
for (int i = 0; i < 3; i++)
{
   cout << "name=" << ptr[i] << endl; 
}

7、指向指针的指针

指向指针的指针,听起来好绕的样子,但是带入生活的案例可能好理解一些,指针对应着地址,比方说我想要认识甲,但是我不知道他的地址,但我知道乙的地址,乙知道甲的地址,那么我通过乙去找甲,这便是指针的指针,像极了人际关系网

在这里插入图片描述
我和乙都是指针,我们掌握的都是地址,而真正需要的东西在甲手里

声明指针的指针需要使用**

int **ptr;

看看简单的使用:

int **pptr;
int *ptr;
int value = 100;

ptr = &value;   // 乙知道甲的地址
pptr = &ptr;   // 我知道乙的地址

cout << value << endl;   // 甲手里的100
cout << *ptr << endl;    // 乙找到甲得到100
cout << **pptr << endl;   // 我找到乙,通过乙找到甲得到100

8、传递指针给函数

C++ 允许传递指针给函数,只需要简单地声明函数参数为指针类型即可

int getSum(int *arr, int size) {
    int sum = 0;
    for (int i = 0; i < size; i++)
    {
        sum += arr[i];
    }
    return sum;
}

int main()
{
   int arr[] = {1, 2, 3, 4, 5};
   cout << getSum(arr, 5) << endl; 
}

9、从函数返回指针

C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量

int *getArray( )
{
  static int  arr[3] = {1, 2, 3};   // 局部变量必须是static
  return arr;
}
 
int main ()
{
   int *p;
 
   p = getArray();
   for ( int i = 0; i < 3; i++ )
   {
       cout << *(p + i) << endl;
   }
 
   return 0;
}

使用static是为了防止局部变量在函数结束的时候失效,随函数一同出栈,使用static会将该变量放到全局区,即使方法栈结束变量依然有效

先看这么多,估计够消化一阵子了,其余的在实践中慢慢学吧~~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32基础篇:RTC × Unix时间戳 × BKP

Unix时间戳 最早是在Unix系统使用的&#xff0c;之后很多由Unix演变而来的系统也都继承了Unix时间戳的规定。目前&#xff0c;Linux、Windows、安卓这些系统&#xff0c;其底层的计时系统都是使用Unix时间戳。 Uinx时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/…

论文速读|ReKep:空间时间理论的关系关键点约束,用于机器人操作

项目地址&#xff1a;ReKep | Spatio-Temporal ReasoningReKep | Spatio-Temporal Reasoning of Relational Keypoint Constraints for Robotic ManipulationReKep | Spatio-Temporal Reasoning ReKep&#xff08;Relational Keypoint Constraints&#xff09;是一种基于视觉的…

Nebula: 无服务器计算中的性能和能源效率 - WebAssembly与Docker的比较研究

这篇论文的标题是《Nebula: Performance and Energy Efficiency in Serverless Computing - A Comparative Study of WebAssembly and Docker》&#xff0c;作者是 Marius Nilsen Kluften&#xff0c;来自奥斯陆大学&#xff08;University of Oslo&#xff09;的信息学系。论文…

android仿assistivetouch悬浮窗实现(带功能实现)

一、悬浮窗点击后的界面&#xff1a; 主要有四个功能&#xff0c;返回、应用程序、退出和主界面。其他功能也可以类似添加。 界面布局代码就不贴出来了&#xff0c;源码&#xff08;切记需要签名才能让功能实现&#xff09;&#xff1a;下载地址 二、主要是检测系统启动或者a…

时序数据库荣登巅峰,被央视报道了!

8月30日&#xff0c;事务处理性能委员会TPC正式公布了最新的国际权威数据库性能基准榜单&#xff0c;“清华系”发起研制的Apache IoTDB开发的国产化时序数据库软件TimechoDB&#xff0c;在性能和系统成本维度上双双打破世界纪录。在央视《24小时》节目中&#xff0c;1分34秒重…

《黑神话:悟空》与游戏经济学的深度剖析

《黑神话&#xff1a;悟空》作为近年来备受瞩目的国产3A游戏大作&#xff0c;自其发布以来&#xff0c;不仅在游戏界内引起了轰动&#xff0c;更在多个消费领域产生了深远的影响。这款游戏不仅以其卓越的品质和深刻的文化内涵吸引了大量玩家的关注&#xff0c;还通过一系列连锁…

神策埋点 sensorsdata.es6.min.js、sensorsdata.min.js 触发eslint 语法检查,导致打包不成功

问题描述&#xff1a; 在使用神策埋点时&#xff0c;下载的web js sdk&#xff0c;打包时eslint 语法检查&#xff0c;会导致打包不成功。npm start没问题。 主要错误是&#xff1a; Line 1:204272: Expected an assignment or function call and instead saw an expression …

[Python]之深拷贝与浅拷贝

Python之深拷贝与浅拷贝 概述: ​ 大白话解释就是 深拷贝拷贝的多, 浅拷贝拷贝的少. 深浅拷贝区别就是: 拷贝的层级的多与少. 深浅拷贝都能操作可变类型 和 不可变类型, 但是深浅拷贝一般操作的都是 可变类型, 几乎不会出操作不可变类型的. 可变类型 和 不可变类型的划分依…

openssl RSA 密钥(key)、证书签名请求(csr)、证书(cer)的生成和例子

1. RSA 密钥(key)、证书签名请求&#xff08;csr&#xff09;、证书&#xff08;cer&#xff09;的生成顺序 2. 具体操作 a. 生成 RSA 密钥&#xff08;私钥&#xff09; openssl genrsa -aes256 -out ca.key 2048b. 生成证书签名请求(csr) # C-----国家&#xff08;Country…

Oracle超详细(数据库编程)

目录 一、数据类型 &#xff08;一&#xff09;数值型数据类型 &#xff08;二&#xff09;字符型数据类型 &#xff08;三&#xff09;日期和时间型数据类型 &#xff08;四&#xff09;大对象和二进制数据类型 &#xff08;五&#xff09;其他数据类型 &#xff08;六&…

离散余弦变换(Discrete Cosine Transform, DCT),信号去噪

介绍 离散余弦变换&#xff08;Discrete Cosine Transform, DCT&#xff09;是一种常用的信号处理工具&#xff0c;特别是在数据压缩、图像处理和模式识别等领域中。DCT的基本思想是将信号从空间域或时间域转换到频率域&#xff0c;以揭示信号中的频率成分。与离散傅里叶变换&…

C/C++网络编程--文件分块传输

文件分块传输是网络编程中一个常见的任务&#xff0c;尤其是在处理大文件时&#xff0c;将文件分块可以提高传输效率&#xff0c;简化错误处理&#xff0c;并可以实现并发传输。下面&#xff0c;写个从客户端向服务器发送大型数据的demo。 客户端 客户端有两点需要注意&#…

vue组件中的数据传递(2)--子组件传父组件

两种情况 子主动传 vue 父传子 子传父实现方式_vue父传子-CSDN博客 vue父子组件传值&#xff0c;父传子&#xff0c;子传父_父传子 且随时变化-CSDN博客 父主动要 Vue2.0的三种常用传值方式、父传子、子传父、非父子组件传值_父传子传-CSDN博客

File Transfer Server 文件传输服务器插件

您需要在本地不同设备之间传输文件吗?现在你可以做到了,你不必安装任何专用服务器。 文件传输服务器为您的游戏或应用程序添加了将文件从任何受支持的平台传输到任何受支持平台的能力。从移动到独立,从移动到移动等(查看支持的平台) 优势: -完整的源代码可用。 -不需要预…

【揭秘心梗元凶】不容忽视的七大生活习惯,竟是心梗“幕后推手”!

在这个快节奏的时代&#xff0c;心梗&#xff08;急性心肌梗死&#xff09;这一健康杀手正悄然逼近&#xff0c;威胁着越来越多人的生命安全。心梗不仅发病急骤&#xff0c;后果往往也极为严重。那么&#xff0c;心梗究竟是如何引起的&#xff1f;今天&#xff0c;我们就来揭开…

Leetcode面试经典150题-239.滑动窗口最大值

解法都在代码里&#xff0c;不懂就留言或者私信 官方定级hard难度&#xff0c;其实是medium&#xff0c;确实字节考过 class Solution {public int[] maxSlidingWindow(int[] nums, int k) {if(nums.length 1) {return new int[]{nums[0]};}/**我们要返回的是一个数组&#…

SoM的理解

对于终端客户来说&#xff0c;要思考到底怎么做一款产品。目前好像主流的就是SoC和SoM。以前联发科是有Turnkey项目&#xff0c;不过我记得我参与的项目&#xff0c;都是直接拿原厂的参考设计&#xff0c;基本上就是改一个壳&#xff0c;电路板&#xff0c;IO啥的都不动&#x…

土壤湿度传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 三、程序设计 main.c文件 TS.h文件 TS.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 传感器适用于土壤的湿度检测&#xff0c;模块中蓝色的电位器是用于土壤湿度的阈值调节&#xff0c;数字量输出DO可以与…

进程间的通信(IPC)基础了解,匿名管道使用,有名管道使用

进程间通信基本知识 进程间通信的定义 进程间通信方式分类 匿名管道&#xff08;pipe&#xff09; 匿名管道介绍 创建方式&#xff1a;使用 pipe 系统调用创建&#xff0c;返回一对文件描述符&#xff08;读端和写端&#xff09;。生命周期&#xff1a;匿名管道的生命周期与…

为什么说RAG是AI 2.0时代的“杀手级”应用?

复旦AI博士&#xff0c;分享AI领域全维度知识与研究。应极客时间邀请开设《RAG快速开发实战》课程&#xff0c;感兴趣的同学可以访问关注 https://time.geekbang.com/column/intro/100804101 随着 AI 2.0 时代的来临&#xff0c;我们正站在一个技术革新和行业变革的交汇点。大语…