C++指针的使用

news2024/12/23 12:21:32

文章目录

  • 1.C++指针
    • 1.1 定义指针
    • 1.2 使用指针
  • 2.空指针和野指针
    • 2.1 空指针
    • 2.2 野指针
  • 3.指针所占空间
  • 4.使用const修饰指针
    • 4.1 const修饰指针
    • 4.2 const修饰常量
    • 4.3 const 既修饰指针也修饰常量
  • 5.指针操作数组
  • 6.指针做函数参数
  • 7.使用指针知识实现冒泡排序

1.C++指针

指针其实就是一块地址,可以用来保存我们想要访问或者修改的变量地址,当我们需要访问或者修改保存的地址时,直接通过指针变量去操作就可以了。

1.1 定义指针

定义指针的语法也很简单:数据类型 *指针变量名; ,比如定义一个整型指针变量: int* p; ,假设我们要为指针变量赋值,我们需要用的取地址符&,如下面代码所示:

  int a = 10;
	int* p;
	p = &a;

如上面代码所示,我们定义了一个变量a,一个指针变量p,并且把a的地址保存到指针变量p中。

1.2 使用指针

我们定义了指针变量并且赋值后应该如何读取指针变量保存的地址对应的值呢?也就是假设我们定义了一个整型变量a,赋值为10,一个指针变量p,将变量a的地址赋给p,这时我们应该如何去通过指针p访问修改a的值呢?这时就需要用到指针的解引用了,需要用到符号*,代码如下所示:

#include<iostream>
using namespace std;

int main() {
	int a = 10;
	// 定义指针语法:数据类型 *指针变量名;
	int* p;
	// 让指针记录变量a的地址
	p = &a;

	// 2.使用指针
	cout << "a的值未被修改前:" << a << endl;
	*p = 100;
	cout << "a的地址: " << (int) & a << endl;
	cout << "指针p为:" << p << endl;
	cout << "指针指向的值为:a= "<<a<< " ,*p = " << *p << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述如上面代码所示,为了对比,我们打印出了变量a之前的值,并且我们打印出了指针p的值,可以看到指针p是一个地址,然后是使用*p = 100的方式访问并修改了指针变量中保存的地址对应的值,而指针变量p中保存的地址正是a的地址,所以修改的其实是a的值。所以a的值变成了100;

注意:可以通过解引用的方式来找到指针指向的内存,*p的方式就是解引用,这样可以访问指针变量中保存的地址对应的值

2.空指针和野指针

2.1 空指针

空指针就是指针变量指向的内存编号为0的空间,我们可以使用空指针来初始化指针变量,比如:
int *p = NULL;,这里需要注意的是,空指针指向的内存是不可以被访问的,例如:

int *p = NULL;
*p = 100;// 错误

上面的代码会导致程序报空指针异常:
在这里插入图片描述这是因为空指针指向的内存是不可以访问的,0~255之间的内存是系统占用的,不可以访问,访问的话就会报异常。

2.2 野指针

野指针就是指针变量指向非法的内存空间,例如我们定义了一个指针变量:int* p = (int*)0x1100;,而0x1100这个地址我们不知道是谁的,就胡乱指的,这种情况特别危险,假设这块地址有重要数据,我们随便一指,然后一改,就会导致重要数据丢失,所以野指针需要被避免。假设我们强制访问未知内存,则会报异常:
在这里插入图片描述
所以我们尽量去访问自己申请的空间,空指针和野指针都不是我们申请的空间,因此不要访问

3.指针所占空间

现在我们知道了指针其实就是一块地址,它用于保存操作其他变量的地址,简单说它就是用来保存地址的,所以它占的空间不需要太大,根据操作系统的位数不同,指针占的空间也不一样,在32位操作系统上,指针占4个字节,在64位操作系统上,指针占8个字节,所有的类型都一样,不管是整型指针,还是浮点型指针,都是一样的,验证的代码如下所示。

#include<iostream>
using namespace std;

int main() {
	// 指针所占内存空间,32位操作系统下,占用4个字节。64位下占用8个字节
	cout << "size of (int*)" << sizeof(int *) << endl;
	cout << "size of (float*)" << sizeof(float *) << endl;
	cout << "size of (double*)" << sizeof(double *) << endl;
	cout << "size of (char*)" << sizeof(char*) << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述如上面代码和运行结果可知,各个类型的指针都是占8个字节,因为我的操作系统是64位的。读者也可以验证下自己的操作系统下指针占多上字节。

4.使用const修饰指针

我们都知道,const是表示常量的意思,在C++中,被这个关键字修饰的变量是不可以被修改的,和Java的final关键字一样,因为指针可以访问并修改内存中的值,这样可能会引起安全问题,所以可以使用const关键字控制指针对内存的修改。const修饰指针主要有三种:

4.1 const修饰指针

const修饰指针,称为常量指针,例如:const int *p;,常量指针的特点就是,指针指向的值不可以被修改,但是指针的指向可以修改,如下代码所示:

#include<iostream>
using namespace std;
int main() {
	// const 修饰指针,常量指针
	int a = 10;
	int b = 10;
	const int* p = &a;
	// 指针指向的值不可以改,指针的指向可以改
  *p = 20;// 错误,常量指针指向的值不可以修改,编译会报错
	p = &b;//正确,常量指针指向的值可以修改
	system("pause");
	return 0;
}

如果强制修改无法编译通过

4.2 const修饰常量

const修饰常量,称为指针常量,例如:int * const p;,指针常量的特点就是:指针的指向不可以改,指针指向的值可以改,如下面代码所示:

#include<iostream>
using namespace std;
int main() {
	int a = 10;
	int b = 10;
	int* const p2 = &a;
	*p2 = 20; // 正确,指针常量指向的值可以修改
	 p2 = &b; // 错误 指针常量的指向不可以改,编译会报错
	system("pause");
	return 0;
}

4.3 const 既修饰指针也修饰常量

当const既修饰指针也修饰常量的时候,例如: const int * const p;,这时候指针的指向和值都不允许修改。如下代码所示:

#include<iostream>
using namespace std;
int main() {
	int a = 10;
	int b = 10;
	const int* const p3 = &a;
	*p3 = 100;// 错误
	p3 = &b; // 错误
	system("pause");
	return 0;
}

5.指针操作数组

指针其实也可以用来访问数组,遍历数组,在C++中,数组名称就是数组的首地址,而数组的存储是连续的,所以我们只要把这个首地址给到指针变量,就可以操作遍历这个数组了,如下面代码所示:

#include<iostream>
using namespace std;
int main() {
	// 利用指针访问数组中的元素
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	cout << "first Element:" << arr[0] << endl;

	int* p = arr;//数组名就是数组的首地址
	cout << "指针访问第一个元素:" << *p << endl;
	p++; // 让指针向后偏移一个元素所占的字节数
	cout << "指针访问第二个元素: " << *p << endl;
	cout << "利用指针遍历数组" << endl;
	int* p2 = arr;
	for (int i = 0; i < 10; i++) {
		cout << *p2 << endl;
		p2++;
	}
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

6.指针做函数参数

指针可以用来做函数的参数,这样可以实现真正的操作传进来的参数原始实参的值,我们给函数传递参数时有两种方式,一种是值传递,传递过去的是值,这个值的修改不会影响原始的实际值,另一种方式是地址传递,这种传递方式传递的是地址,假设我们修改了这个地址对应的值,那么就会影响原始变量的值。举个例子,实现两个数交换,我们使用值传递的方式代码如下:

#include<iostream>
using namespace std;
void swap(int a, int b) {
	int tmp = a;
	a = b;
	b = tmp;

	cout << "swap: a= " << a << endl;
	cout << "swap: b= " << b << endl;
}
int main() {
	// 指针作为函数参数
	// 1、值传递
	int a = 10;
	int b = 20;
	swap(a, b);
	// 实参没有改变
	cout << "a= " << a << endl;
	cout << "b= " << b << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述我们可以看到,虽然在swap函数种的值已经交换了,但是我们的原始变量a,b的值还是没有变化。我们使用地址传递的方式,代码如下:

#include<iostream>
using namespace std;
void swap1(int* p1, int* p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
int main() {
	int a = 10;
	int b = 20;
	// 地址传递: 可以修改实参
	swap1(&a, &b);
	cout << "地址传递:a= " << a << endl;
	cout << "地址传递:b= " << b << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
如上面的代码所示,我们传递参数的时候将参数的地址传递给函数,在swap1函数中解析出地址对应的值并且做交换,这样就能修改原始实参的值了。所以指针做函数参数其实就是一种地址传递。

7.使用指针知识实现冒泡排序

学习完指针,我们用指针做一个冒泡排序的算法。代码如下所示:

#include<iostream>
using namespace std;
// 参数1:数组首地址,参数2:数组长度
void bubbleSort(int* arr, int len) {
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - i - 1; j++) {
			// 如果j>j+1的值,就交换
			if(arr[j] > arr[j + 1]){
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void printArr(int *arr,int len) {
	for (int i = 0; i < len; i++) {
		cout << arr[i] << endl;
	}
}
int main() {
	// 利用冒泡排序,对整型数组进行升序排序
	// 1.先创建数组
	int arr[10] = { 1,3,2,5,7,1,9,10,7,13 };
	// 2.创建函数,实现冒泡排序
	int len = sizeof(arr) / sizeof(arr[0]);
		bubbleSort(arr, len);
	// 3.打印结果
		printArr(arr,len);
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

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

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

相关文章

基础数据结构之——【顺序表】(上)

从今天开始更新数据结构的相关内容。&#xff08;我更新博文的顺序一般是按照我当前的学习进度来安排&#xff0c;学到什么就更新什么&#xff08;简单来说就是我的学习笔记&#xff09;&#xff0c;所以不会对一个专栏一下子更新到底&#xff0c;哈哈哈哈哈哈哈&#xff01;&a…

掌动智能:替代JMeter的压力测试工具有哪些

JMeter是一个广泛使用的开源压力测试工具&#xff0c;但在实际应用中&#xff0c;也有一些其他优秀的替代品可供选择。本文将介绍几个可替代JMeter的压力测试工具&#xff0c;它们在功能、性能和易用性方面都具有独特优势&#xff0c;可以满足不同压力测试需求的选择。 一、Gat…

使用ExLlamaV2在消费级GPU上运行Llama2 70B

Llama 2模型中最大也是最好的模型有700亿个参数。一个fp16参数的大小为2字节。加载Llama 270b需要140 GB内存(700亿* 2字节)。 只要我们的内存够大&#xff0c;我们就可以在CPU上运行上运行Llama 2 70B。但是CPU的推理速度非常的慢&#xff0c;虽然能够运行&#xff0c;速度我…

[管理与领导-108]:IT人看清职场中的隐性规则 - 5 - 你会在不经意间被归属在不同的分类中,一旦分类定型,你就会被打上了某种标签(职场分类方法大全)

目录 前言&#xff1a; 一、关于分类 1.1 什么是分类 1.2 分类是人们理解复杂问题的一种常见方式 1.3 分类的优点与缺点 1.4 职场中的分类方法 二、职场对人的分类方法1&#xff1a;组织架构 2.1 职位和职级分类 2.2 按照部门、岗位进行分类 三、职场对人的分类方法2…

java Spring Boot按日期 限制大小分文件记录日志

上文 java Spring Boot 将日志写入文件中记录 中 我们实现另一个将控制台日志写入到 项目本地文件的效果 但是 这里有个问题 比如 我项目是个大体量的企业项目 每天会有一百万用户访问 那我每天的日志都记载同一个文件上 那不跟没记没什么区别吗&#xff1f; 东西怎么找&#x…

C++11之可变参数模板

可变参数模板 可变参数模板概念可变参数模板定义参数包展开方式递归展开参数包逗号表达式展开参数包 STL容器中的emplace相关接口函数 可变参数模板概念 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板&#xff0c;相比C98/03&#xff0c;类模版和函…

Java进阶02 Array、内存分析、this、面向对象、继承、override、super、实例化、多态、向下转型、Object

文章目录 一、数组(Array)二、数组的内存分析三、Array工具类四、面向对象的一些小知识五、进阶知识补充1. this关键字2.继承3.方法重写4.super关键字的使用5.子类对象实例化6.多态性的体现7.向下转型8.Object类 一、数组(Array) 数组&#xff1a;多个相同类型数据按照一定顺序…

基于 SpringBoot 2.7.x 使用最新的 Elasticsearch Java API Client 之 ElasticsearchClient

1. 从 RestHighLevelClient 到 ElasticsearchClient 从 Java Rest Client 7.15.0 版本开始&#xff0c;Elasticsearch 官方决定将 RestHighLevelClient 标记为废弃的&#xff0c;并推荐使用新的 Java API Client&#xff0c;即 ElasticsearchClient. 为什么要将 RestHighLevelC…

大喜国庆,聊聊我正式进入职场的这三个月...

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

Thymeleaf 内联语法使用教程

1 表达式内联 Thrmeleaf标准方言允许使用标签属性(th:)来实现很多的功能&#xff0c;但在有些场景之下&#xff0c;需要将表达式直接写入我们HTML 代码中和CSS代码中及JavaScript代码中【代码和html文件在一起&#xff0c;分能不开&#xff0c;待验证】&#xff0c;称为内联…

[Unity][VR]Oculus透视开发图文教程1-Passthrough应用XR项目设置

Oculus现在已向开发者公布了如何使用自己的设备Camera,本系列课程就来手把手地告诉你如何在Unity中使用这个特性。 第一步,既然用的是Quest的特性,那就需要先引入Quest的Unity开发SDK。并且完成基本的VR开发项目设置。 新建Unity项目后,在编辑器界面先点击Window,打开资…

【实验记录】一些小疑问

1.为什么要选择基于“外观”这一特性来作为回环检测的方案&#xff1f; 朴素思路复杂度高&#xff0c;不利于实时性&#xff1b;基于“里程计”的方案需要知道相机处于何位置下才能发生检测&#xff0c;这与我们需要知道的准确位置相矛盾 基于“外观”的方案与前端和后端均无关…

计算机图形学、贝塞尔曲线及绘制方法、反走样问题的解决(附完整代码)

贝塞尔曲线 1. 本次作业实现的函数及简单描述&#xff08;详细代码见后&#xff09;2. 与本次作业有关的基础知识整理3. 代码描述&#xff08;详细&#xff09;4. 完整代码5. 参考文献 &#xff08;本篇为作者学习计算机图形学时根据作业所撰写的笔记&#xff0c; 如有同课程请…

进程的状态与转换以及组织方式

1.进程的状态 三种基本状态&#xff1a;运行态&#xff0c;就绪态&#xff0c;阻塞态。 1.运行状态 如果一个进程此时在CPU上运行&#xff0c;那么这个进程处于“运行态”。 CPU会执行该进程对应的程序&#xff08;执行指令序列) 2.就绪状态 当进程创建完成后&#xff0c;…

【论文阅读】(CVPR2023)用于半监督医学图像分割的双向复制粘贴

目录 前言方法BCPMean-teacher and Traning StrategyPre-Training via Copy-PasteBidirectional Copy-Paste ImagesBidirectional Copy-Paste Supervisory Signals Loss FunctionTesting Phase 结论 先看这个图&#xff0c;感觉比较清晰。它整个的思路就是把有标签的图片和无标…

动态规划算法(1)--矩阵连乘

目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘&#xff1f; 2、动态规划思路 3、手推m和s矩阵 4、完…

AI伦理与机器道德:人工智能的道德挑战

文章目录 什么是AI伦理和机器道德&#xff1f;1. 隐私保护2. 歧视和不平等3. 透明度和解释性4. 安全性5. 社会影响 AI伦理和机器道德的重要性1. 保护个人权利2. 避免不平等和歧视3. 保持透明和责任4. 促进创新 AI伦理挑战和解决方案1. 隐私保护2. 歧视和不平等3. 透明度和解释性…

P1541 [NOIP2010 提高组] 乌龟棋(4维背包问题)

[NOIP2010 提高组] 乌龟棋 题目背景 小明过生日的时候&#xff0c;爸爸送给他一副乌龟棋当作礼物。 题目描述 乌龟棋的棋盘是一行 N N N 个格子&#xff0c;每个格子上一个分数&#xff08;非负整数&#xff09;。棋盘第 1 1 1 格是唯一的起点&#xff0c;第 N N N 格是…

Nginx简介与Docker Compose部署指南

Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;以其卓越的性能、可伸缩性和灵活性而闻名。它在全球范围内广泛用于托管Web应用程序、负载均衡、反向代理和更多场景中。在本文中&#xff0c;我们将首先介绍Nginx的基本概念&#xff0c;然后演示如何使用Docker C…

Apollo自动驾驶系统概述(文末参与活动赠送百度周边)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…