C++ 指针,数组与指针之间的关系以及指针运算

文章目录

  • C++ 指针
    • C++ 中使用指针
  • C++ Null 指针
  • 数组与指针
    • sizeof在数组和指针的上的区别
      • &intArr 和 &intArr[0]的类型
  • C++ 指针的算术运算
    • 递增一个指针
    • 递减一个指针
    • 指针的比较
  • 引用 VS 指针
    • C++ 把引用作为参数
    • C++ 把引用作为返回值
  • 指针的释放

C++ 指针

学习 C++ 的指针既简单又有趣。通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C++ 程序员,学习指针是很有必要的。

正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:

#include <iostream>using namespace std;int main ()
{int  var1;char var2[10];cout << "var1 变量的地址: ";cout << &var1 << endl;cout << "var2 变量的地址: ";cout << &var2 << endl;return 0;
}
//输出内容
// var1 变量的地址: 0xbfebd5c0
// var2 变量的地址: 0xbfebd5b6

C++ 中使用指针

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include <iostream>using namespace std;int main ()
{int  var = 20;   // 实际变量的声明int  *ip;        // 指针变量的声明ip = &var;       // 在指针变量中存储 var 的地址cout << "Value of var variable: ";cout << var << endl;// 输出在指针变量中存储的地址cout << "Address stored in ip variable: ";cout << ip << endl;// 访问指针中地址的值cout << "Value of *ip variable: ";cout << *ip << endl;return 0;
}//输出内容// Value of var variable: 20
// Address stored in ip variable: 0xbfc601ac
// Value of *ip variable: 20

C++ Null 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

#include <iostream>using namespace std;int main ()
{int  *ptr = NULL;cout << "ptr 的值是 " << ptr ;return 0;
}//输出内容
// ptr 的值是 0

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */

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

数组与指针

先看如下代码

string s = "Hello world";
cout << s << endl;//测试数组前面加个指针运算符什么意思
int var[] = { 100,200,300,400 };
int* ptr = var;for (int i = 0; i < sizeof(var) / 4; i++) {cout << "var["<<i<<"]的值是  :"<< var[i]<< endl;cout << "var[" << i << "]的地址是:" << &var[i] << endl;*var = i;cout << "var[" << i << "]的*是:  " << var << endl;
}//输出内容
/*
Hello world
var[0]的值是  :100
var[0]的地址是:0000008B8ABEF678
var[0]的*是:  0
var[1]的值是  :200
var[1]的地址是:0000008B8ABEF67C
var[1]的*是:  1
var[2]的值是  :300
var[2]的地址是:0000008B8ABEF680
var[2]的*是:  2
var[3]的值是  :400
var[3]的地址是:0000008B8ABEF684
var[3]的*是:  3
*/

*c++ 中 int var[] = {1,3}; 那么 var 是什么意思?

*在C++中,`int var[] = {1,3};` 定义了一个名为 `var` 的整数数组,并初始化了两个元素。数组 `var` 的第一个元素是1,第二个元素是3。*

*`*var` 表示数组 `var` 的首元素的地址。具体来说,`var` 是一个数组名,它本质上是一个指向数组首元素的常量指针。因此,`*var` 就是解引用这个指针,即获取数组首元素的值。*

*对于上面的例子,`*var` 的值是1,即数组 `var` 的第一个元素的值。*

指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。请看下面的程序

#include <iostream>using namespace std;
const int MAX = 3;int main ()
{int  var[MAX] = {10, 100, 200};int  *ptr;// 指针中的数组地址ptr = var;for (int i = 0; i < MAX; i++){cout << "var[" << i << "]的内存地址为 ";cout << ptr << endl;cout << "var[" << i << "] 的值为 ";cout << *ptr << endl;// 移动到下一个位置ptr++;}return 0;
}
/*输出结果
var[0]的内存地址为 0x7fff59707adc
var[0] 的值为 10
var[1]的内存地址为 0x7fff59707ae0
var[1] 的值为 100
var[2]的内存地址为 0x7fff59707ae4
var[2] 的值为 200
*/

*C++ int var[MAX] = {10, 100, 200}; (var + 2) = 500; 什么意思

在C++中,int var[MAX] = {10, 100, 200}; 定义了一个名为 var 的整数数组,并初始化了三个元素。数组 var 的第一个元素是10,第二个元素是100,第三个元素是200。

*(var + 2) 是数组元素访问的一种方式。在这里,var 是一个指向数组首元素的指针,因此 var + 2 是指向数组第三个元素的指针。* 操作符用于解引用指针,即获取指针所指向的值。

所以,*(var + 2) = 500; 这行代码的意思是将数组 var 的第三个元素的值设置为500。

执行这行代码后,数组 var 的内容变为:

var[0] = 10; // 第一个元素  
var[1] = 100; // 第二个元素  
var[2] = 500; // 第三个元素

sizeof在数组和指针的上的区别

如下代码,sizeof是有去别的

int arr[10];
int* p = arr;//得到的是数组的长度*int的字符数(一般是4)
int len1 = sizeof(arr);
//得到的是指针的长度,一般是2或者4的hex
int len1 = sizeof(p);

但是在使用过程中arr[0]和p[0]是一样的,而且这两个是可以通用的,为什么会出现这样的情况呢,那就是因为arr的地址是整个数组的地址,而不是首元素的地址,然而p是数组首元素的地址。虽然在输出上 p 和 arr的数值都是一样的。

比如看如下代码:

 int intArr[10];int* pInt = intArr;short shortArr[10];short* pShort = shortArr;cout << "intArr的地址是" << &intArr << endl;cout << "intArr[0]的地址是" << &intArr[0] << endl;cout << "pInt 是" << pInt << endl;cout << "shortArr的地址是" << &shortArr << endl;cout << "shortArr[0]的地址是" << &shortArr[0] << endl;cout << "pShort 是" << pShort << endl;/*输出内容
intArr的地址是00000018682FFA88
intArr[0]的地址是00000018682FFA88
pInt 是00000018682FFA88
shortArr的地址是00000018682FFAE8
shortArr[0]的地址是00000018682FFAE8
pShort 是00000018682FFAE8
*/

根据上面的输出内容开起来好像指针和数组地址和数组首元素地址是一样的,因为输出的内容相同,那么接下来做个指针运算试一试

int intArr[10];
int* pInt = intArr;short shortArr[10];
short* pShort = shortArr;
cout << "intArr的地址是" << &intArr << endl;
cout << "intArr[0]的地址是" << &intArr[0] << endl;
cout << "pInt 是" << pInt << endl;
cout << "shortArr的地址是" << &shortArr << endl;
cout << "shortArr[0]的地址是" << &shortArr[0] << endl;
cout << "pShort 是" << pShort << endl;//指针运算
pInt += 1;
cout << "pInt++ 是" << pInt << endl;
cout << "intArr[1]的地址是" << &intArr[1] << endl;
cout << "intArr[0]的地址是" << &intArr[0] << endl;
auto x = &intArr;
x += 1;
cout << "&intArr[0]的类型是" << typeid(&intArr[0]).name() << endl;
cout << "&intArr的类型是" << typeid(&intArr).name() << endl;
cout << "&intArr+1的地址是" << x << endl;/*输出内容
intArr的地址是0000003F30FEF948
intArr[0]的地址是0000003F30FEF948
pInt 是0000003F30FEF948
shortArr的地址是0000003F30FEF9A8
shortArr[0]的地址是0000003F30FEF9A8
pShort 是0000003F30FEF9A8
pInt++ 是0000003F30FEF94C
intArr[1]的地址是0000003F30FEF94C
intArr[0]的地址是0000003F30FEF948
&intArr[0]的类型是int * __ptr64
&intArr的类型是int (* __ptr64)[10]
&intArr+1的地址是0000003F30FEF970
*/

&intArr 和 &intArr[0]的类型

通过上述代码发现这个的类型是有不同的,一个是(* __ptr64)[10]另外一个是 * __ptr64,这应该就是sizeof得到的长度不一样的原因了,这也说明了另外一个问题 &intArr != &intArr[0]

C++ 指针的算术运算

指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

递增一个指针

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

#include <iostream>using namespace std;
const int MAX = 3;int main ()
{int  var[MAX] = {10, 100, 200};int  *ptr;// 指针中的数组地址ptr = var;for (int i = 0; i < MAX; i++){cout << "Address of var[" << i << "] = ";cout << ptr << endl;cout << "Value of var[" << i << "] = ";cout << *ptr << endl;// 移动到下一个位置ptr++;}return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Address of var[0] = 0xbfa088b0
Value of var[0] = 10
Address of var[1] = 0xbfa088b4
Value of var[1] = 100
Address of var[2] = 0xbfa088b8
Value of var[2] = 200

递减一个指针

同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:

#include <iostream>using namespace std;
const int MAX = 3;int main ()
{int  var[MAX] = {10, 100, 200};int  *ptr;// 指针中最后一个元素的地址ptr = &var[MAX-1];for (int i = MAX; i > 0; i--){cout << "Address of var[" << i << "] = ";cout << ptr << endl;cout << "Value of var[" << i << "] = ";cout << *ptr << endl;// 移动到下一个位置ptr--;}return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Address of var[3] = 0xbfdb70f8
Value of var[3] = 200
Address of var[2] = 0xbfdb70f4
Value of var[2] = 100
Address of var[1] = 0xbfdb70f0
Value of var[1] = 10

指针的比较

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:

#include <iostream>using namespace std;
const int MAX = 3;int main ()
{int  var[MAX] = {10, 100, 200};int  *ptr;// 指针中第一个元素的地址ptr = var;int i = 0;while ( ptr <= &var[MAX - 1] ){cout << "Address of var[" << i << "] = ";cout << ptr << endl;cout << "Value of var[" << i << "] = ";cout << *ptr << endl;// 指向上一个位置ptr++;i++;}return 0;
}

引用 VS 指针

引用很容易与指针混淆,它们之间有三个主要的不同:

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。

demo

#include <iostream>using namespace std;int main ()
{// 声明简单的变量int    i;double d;// 声明引用变量int&    r = i;double& s = d;i = 5;cout << "Value of i : " << i << endl;cout << "Value of i reference : " << r  << endl;d = 11.7;cout << "Value of d : " << d << endl;cout << "Value of d reference : " << s  << endl;return 0;
}
/*输出内容
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
*/

C++ 把引用作为参数

#include <iostream>
using namespace std;// 函数声明
void swap(int& x, int& y);int main ()
{// 局部变量声明int a = 100;int b = 200;cout << "交换前,a 的值:" << a << endl;cout << "交换前,b 的值:" << b << endl;/* 调用函数来交换值 */swap(a, b);cout << "交换后,a 的值:" << a << endl;cout << "交换后,b 的值:" << b << endl;return 0;
}// 函数定义
void swap(int& x, int& y)
{int temp;temp = x; /* 保存地址 x 的值 */x = y;    /* 把 y 赋值给 x */y = temp; /* 把 x 赋值给 y  */return;
}
/*输出内容
交换前,a 的值: 100
交换前,b 的值: 200
交换后,a 的值: 200
交换后,b 的值: 100
*/

C++ 把引用作为返回值

当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。

静态引用demo

int& func() {int q;//! return q; // 在编译时发生错误static int x;return x;     // 安全,x 在函数作用域外依然是有效的
}

指针的释放

指针的释放(delete)只能释放用new声明的指针,不能释放将一个变量地址给指针的指针,

比如

int a = 123;
int* p = &a; delete p //这个是不允许的会报错

如下这个是允许的

int* p = new int();delete p;int* p1 = new int[123];
//注意这里释放指向数组的指针
delete [] p1;

对于一个指向NULL的指针使用delete是安全的。

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

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

相关文章

C++ easyX小程序(介绍几个函数的使用)

本小程序通过代码和注释&#xff0c;介绍了easyX窗口及控制台窗口的设置方法&#xff1b;还介绍了easyX中关于颜色、线型、画圆、画方、显示文字以及鼠标消息处理等函数的使用方法。为便于理解&#xff0c;本程序同时使用控制台和easyX窗口&#xff0c;由控制台控制程序运行、由…

SpringBoot常见错误

SpringBoot常见错误 1、SpringBoot启动时报错 错误: 找不到或无法加载主类 com.xxx.xxx.Application springboot启动时报错错误&#xff1a;找不到或无法加载主类 com.xxx.xxx.Application。 解决方法就是打开idea的控制台&#xff0c;输入以下三行命令&#xff1a; mvn cl…

nginx安装ssl模块http_ssl_module

查看nginx安装的模块 /usr/local/nginx/sbin/nginx -V若出现“–with-http_ssl_module”说明已经安装过&#xff0c;否则继续执行下列步骤 进入nginx源文件目录 cd /usr/local/nginx/nginx-1.20.2重新编译nginx ./configure --with-http_ssl_module如果组件linux缺少&…

综合案例 - 商品列表

文章目录 需求说明1.my-tag组件封装&#xff08;完成初始化&#xff09;2.may-tag封装&#xff08;控制显示隐藏&#xff09;3.my-tag组件封装&#xff08;v-model处理&#xff1a;信息修改&#xff09;4.my-table组件封装&#xff08;整个表格&#xff09;①数据不能写死&…

《HTML 简易速速上手小册》第3章:HTML 的列表与表格(2024 最新版)

文章目录 3.1 创建无序和有序列表&#xff08;&#x1f4dd;&#x1f31f;&#x1f44d; 信息的时尚搭配师&#xff09;3.1.1 基础示例&#xff1a;创建一个简单的购物清单3.1.2 案例扩展一&#xff1a;创建一个旅行计划清单3.1.3 案例扩展二&#xff1a;创建一个混合列表 3.2 …

uniapp报错:export { render, staticRenderFns, recyclableRender, components }

uniapp vue2项目启动报错 export { render, staticRenderFns, recyclableRender, components }解决办法: 降低prettier的版本 "prettier": "^3.2.4","prettier": "2.8.8",参考 SyntaxError: /xxxx.vue: Unexpected token, expected…

【数据分享】1929-2023年全球站点的逐年平均气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01;本次我们为大家带来的就是具体到气象监…

win11 系统 WSL2 备份与还原

win11 系统想要使用 linu 开发环境&#xff0c;除了虚拟机&#xff0c;就是 wsl 好使了。 但是 wsl 如过用了一段时间里面环境工程配置迁移麻烦如果重装系统后能直接备份还原就方便了。 确定你的版本 使用 WinR 打开输入框 输入 cmd 命令 打开命令提示符界面 wsl -l -v查看…

com.sun.jna.platform.mac.SystemB$Timeval

错误信息 Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jna/platform/mac/SystemB$Timevalat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:757)at java.security.SecureClas…

C++笔记(六)

加号运算符重载&#xff1a; 成员函数重载 person operator(person& p) { person temp; temp.m_a this->m_a p.m_a; temp.m_b this->m_b p.m_b; return temp; } 全局函数重载 Person operator(Person& p1, Person& p2) { Person temp; temp.m_A p1.m_A…

GeoHash编码在日志数据处理中的应用与优化

目录 需求说明 GeoHash编码 GeoHash编码介绍 GeoHash编码的基本原理 GeoHash的UDF函数

秀!巧用Python实现对单个文件或多个文件中的指定字符串进行批量(修改)替换

目录 1. 对单份文件 1.1 将替换后的内容保存到新文件中 1.2 直接替换当前文件中的字符 2. 对多份文件&#xff08;支持递归子目录&#xff09; 1. 对单份文件 示例&#xff1a;将文件中字符串“address”替换成“device.address” 1.1 将替换后的内容保存到新文件中 实现…

【备战蓝桥杯】——循环结构

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-bFHV3Dz5xMe6d3NB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

QT tcp与udp网络通信以及定时器的使用 (7)

QT tcp与udp网络通信以及定时器的使用 文章目录 QT tcp与udp网络通信以及定时器的使用1、QT网络与通信简单介绍2、QT TCP通信1、 服务器的流程2、 客户端的流程3、服务器的编写4、客户端的编写 3、QT UDP通信1、客户端流程2、客户端编写3、UDP广播4、UDP组播 4、定时器的用法1、…

【ArcGIS微课1000例】0098:查询河流流经过的格网

本实验讲述,ArcGIS中查询河流流经过的格网,如黄河流经过的格网、县城、乡镇、省份等。 文章目录 一、加载数据二、空间查询三、结果导出四、注意事项一、加载数据 加载实验配套数据0098.rar中的河流(黄河)和格网数据,如下图所示: 接下来,将查询河流流经过的格网有哪些并…

thinter聊天小工具

# 服务端 import queue import re import socket import time import tkinter as tk from threading import Threadclass ChatService():def __init__(self):# 聊天框左上角名称self.title f"马里奇聊天室"self.tk tk.Tk(classNameself.title)# 隐藏窗口# self.tk.…

社区检测(Community Detection)

如果你想进行社区检测&#xff08;Community Detection&#xff09;&#xff0c;你可以使用 NetworkX 库提供的一些算法。社区检测的目标是将图中的节点划分为若干个社区或群组&#xff0c;使得社区内的节点紧密相连而社区间的连接尽量稀疏。以下是一个使用 Louvain 方法进行社…

SpringBoot将第三方的jar中的bean对象自动注入到ioc容器中

新建一个模块&#xff0c;做自动配置 config&#xff1a;需要准备两个类&#xff0c;一个自动配置类&#xff0c;一个配置类 CommonAutoConfig&#xff1a;此类用于做自动配置类它会去读取resoutces下的META-INF.spring下的org.springframework.boot.autoconfigure.AutoConfig…

LabVIEW探测器CAN总线系统

介绍了一个基于FPGA和LabVIEW的CAN总线通信系统&#xff0c;该系统专为与各单机进行系统联调测试而设计。通过设计FPGA的CAN总线功能模块和USB功能模块&#xff0c;以及利用LabVIEW开发的上位机程序&#xff0c;系统成功实现了CAN总线信息的收发、存储、解析及显示功能。测试结…

图论第二天|695. 岛屿的最大面积 1020. 飞地的数量 130. 被围绕的区域 417. 太平洋大西洋水流问题 827.最大人工岛

目录 Leetcode695. 岛屿的最大面积Leetcode1020. 飞地的数量Leetcode130. 被围绕的区域Leetcode417. 太平洋大西洋水流问题Leetcode827.最大人工岛 Leetcode695. 岛屿的最大面积 文章链接&#xff1a;代码随想录 题目链接&#xff1a;695. 岛屿的最大面积 思路&#xff1a;dfs …