指针的学习1

目录

什么是指针?

野指针

造成野指针的原因:

如何避免野指针?

内存和指针

如何理解编址?

指针变量和地址

取地址操作符&

指针变量和解引用操作符

指针变量

如何拆解指针类型?

指针变量的大小

指针变量类型的意义

指针的解引用

指针+整数

void*指针

void* 类型的指针有什么用呢?

const修饰指针

const修饰变量

const修饰指针变量

指针运算

指针+-整数

指针-指针

指针的关系运算

assert断言

使用assert()有几个好处:

指针的使用和传址调用

strlen的模拟实现

传值调用和传址调用


什么是指针?

指针(pointer)是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值,由于通过地址能找到所需的变量单位,可以说,地址指向该变量单位,因此,将地址形象化的称为“指针”,意思是,通过它能找到以它为地址的内存单元

指针是一个变量,存放内存单元的地址(编号)

#include <stdio.h>
int main()
{int a = 10;//在内存中开辟一组空间int* p = &a;//将a的地址存放在p变量中,p就是一个指针变量return 0;
}

野指针

野指针:指针指向的位置是不可知的(随机的、不正确的、没有明确限制的) 

造成野指针的原因:
  1. 指针未初始化;
  2. 指针越界访问;
  3. 指针指向内存空间释放; 
如何避免野指针?
  • 指针初始化;

如果明确知道指针指向哪里就直接赋值地址,如果不知道指针应该指向哪里,可以给指针赋值NULL

NULL是C语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错

  • 小心指针越界;
  • 指针指向空间释放即使置NULL;指针使用之前检查有效性 

当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL,因为约定俗成的一个规则就是:只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL

  • 避免返回局部变量的地址

内存和指针

计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中

把内存划分为一个个的内存单元,每个内存单元的大小取1个字节

1byte=8bit(bit是最小的内存单元)

一个比特位可以存储一个2进制的位1或者0

内存单元的编号=地址=指针

如何理解编址?

首先,必须理解,计算机内有很多的硬件单元,这些硬件单元要互相协同工作。所谓的协作,至少相互之间要能够进行数据传递

但是,硬件与硬件之间是相互独立的,需要用“线”进行通信

CPU和内存之间也是有大量的数据交互的,所以两者必须用线连起来-地址总线

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而内存中字节很多,需要给内存进行编址

计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的

32位机器有32根地址总线,每根线有两种状态,表示0或1(电脉冲有无),一根线表示2种含义,2根线表示4种含义,32根地址线表示2^32种含义,每一种含义代表一个地址

地址信息被下达给内存,在内存上就可以找到该地址对应的数据,将数据通过数据总线传入CPU内寄存器

指针变量和地址

取地址操作符&

在C语言中创建变量其实就是向内存申请空间

&a取出的是a所占4个字节中地址较小的字节地址

指针--地址

指针变量--存放地址的变量

指针变量和解引用操作符
指针变量

通过取地址操作符(&)拿到的地址是一个数值,这个数值需要存放在指针变量

指针变量也是一种变量,这种变量是用来存放地址的,存放在指针变量中的值都会理解为地址

指针变量中存储的是一个地址,指向同类型的一块内存空间

如何拆解指针类型?
int a=10;
int* pa=&a;

pa左边写的是int*,*说明pa是指针变量,前面的int说明pa指向的是整型(int)类型的对象

int main()
{int a = 10;int* p = &a;*p = 0;//*解引用操作符(间接访问操作符)//*&a=0;printf("%d", a);//0return 0;
}
指针变量的大小

32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或0,把32根地址线产生的2进制序列当作一个地址,那么一个地址就是32个bit位,需要4个字节才能存储

如果指针变量是用来存放地址的,那么指针变量的大小就得是4个字节的空间才可以

同理,64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要8个字节的空间,指针变量的大小就是8个字节

指针变量--存放地址的

一个字节对应一个地址

指针用来存放地址,地址是唯一标示一块地址空间的

总结:

1.32位平台下地址是32个bit位,指针变量大小是4个字节

2.64位平台下地址是64个bit位,指针变量大小是8个字节

3.注意指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下大小都是相同的

指针变量类型的意义

指针的解引用

指针类型决定了指针进行解引用操作时,能够访问空间的大小(对指针解引用的时候有多大的权限--一次能操作几个字节)

int* p;*p能够访问4个字节

char* p;*p能够访问1个字节

doble* p;*p能够访问8个字节

指针+整数

指针类型决定了,指针走一步能走多远(指针的步长,单位是字节)

void*指针

void* 无具体类型的指针(或者泛型指针),这种类型的指针可以用来接受任意类型的地址,但也有局限性,void* 类型的指针不能进行指针的+-整数和解引用的运算

void* 类型的指针有什么用呢?

一般void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果,使得一个函数来处理多种类型的数据

const修饰指针

const修饰变量

变量不再被改变,变量有了常属性,本质仍是变量,常变量(在C++中,const修饰的变量就是常量)

如果要修改变量,就要通过指针来修改,这样就打破了const的限制,是不合理的,所以应该让变量的地址也不能被修改

const修饰指针变量

const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不一样的

int main()
{int a = 10;int b = 20;int* const p = &a;//const在*右边//p=&b;//err*p = 100;printf("%d", a);return 0;
}
  • const限制的是指针变量本身,指针变量不能再指向其他变量了,但是可以通过指针变量,修改指针变量指向的内容
int main()
{int a = 10;int b = 20;int const* p = &a;//const在*右边printf("p=%d\n", p);p=&b;//*p = 100;//errprintf("p=%d", p);return 0;
}

运行结果:

  • const放在*左边,限制的是:指针指向的内容,不能通过指针来修改指向的内容,但是可以修改指针变量本身的值(修改指针变量的指向)

*两边可以同时加const

指针运算

指针的作用就是访问内存的

指针的基本运算有三种:

  • 指针+-整数
  • 指针-指针
  • 指针的关系运算
指针+-整数
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){/*printf("%d ", *p);p++;*///等同于下面一句:printf("%d ", *(p + i));//p没有变化}return 0;
}
指针-指针

指针-指针的绝对值是:两个指针之间的元素个数;指针+指针无意义

计算的前提是:两个指针指向了同一块空间

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(char* str)
{char* start = str;while (*str != '\0'){str++;}return str - start;//指针-指针
}
int main()
{//strlen-求字符串长度,‘\0’之前的字符个数char arr[] = "abcdef";int len = my_strlen(arr);printf("%d", len);return 0;
}
指针的关系运算
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;//&arr[0]while (p < arr + sz){printf("%d ", *p);p++;}return 0;
}

assert断言

assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这个宏常常被称为“断言”

assert(p != NULL);

上面代码在程序运行到这一行语句时,验证变量是否等于NULL,如果不等于NULL,程序继续运行,否则就会终止运行,并且会给出报错信息提示

assert()宏接受一个表达式作为参数,如果该表达式为真(返回值非零),assert()不会产生任何作用,程序继续运行。如果该表达式为假(返回值为0),assert()就会报错,在标准错误流stderr中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号

使用assert()有几个好处:

assert()的使用对程序员是非常友好的,它不仅能自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭assert()的机制。如果已经确认程序没有问题,不需要再做断言,就在#include <assert.h>语句的前面,定义一个宏NDEBUG

#define NDEBUG
#include <assert.h>

然后,重新编译程序,编译器就会禁用文件中所有的assert()语句,如果程序又出现问题,可以移除这条#define NDEBUG指令(或者把它注释掉),再次编译,这样就重新启用了assert()语句

assert的缺点是:因为引入了额外的检查,增加了程序的运行时间

一般我们可以在Debug中使用,在Release版本中选择禁用assert就行,在VS这样的集成开发环境中,在Release版本中,直接优化掉了,这样在Debug版本写有利于程序员排查问题,在Release版本不影响用户使用时程序的效率

指针的使用和传址调用

strlen的模拟实现
size_t my_strlen(const char* str)//size_t无符号的整型
{size_t count = 0;assert(str != NULL);while (*str != '\0'){count++;str++;}return count;
}
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%d", len);return 0;
}
传值调用和传址调用

下面看一个典型代码,曾几何时,我被它坑过:

void Swap(int x, int y)
{int temp = x;x = y;y = temp;
}
int main()
{int a = 10;int b = 20;printf("交换前:a=%d,b=%d\n", a, b);Swap(a, b);//传值调用printf("交换后:a=%d,b=%d", a, b);return 0;
}

当实参传递给形参的时候,形参是实参的一份临时拷贝,对形参的修改不会影响实参

如果要交换两个数的值,参考以下代码:

void Swap(int* pa, int* pb)
{int temp = *pa;*pa = *pb;*pb = temp;
}
int main(){int a = 10;int b = 20;printf("交换前:a=%d,b=%d\n", a, b);Swap(&a, &b);//传址调用printf("交换后:a=%d,b=%d", a, b);return 0;
}

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

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

相关文章

麒麟系统—— openKylin 安装 Maven

麒麟系统—— openKylin 安装 Maven 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。2. 确保 java 已经安装完毕 二、下载Maven三、解压 Maven 与环境配置解压配置环境变量验证 最终&#xff1a;介绍配置的其他参数使用 本文将分享如何在麒麟操作系统 openKylin 上安装…

【Spring源码分析】推断构造方法

推断构造方法源码解析 一、确认候选构造——AutowireAnnotationBeanPostProcessor#determineCandidateConstructors二、autowireConstructor 方法源码解析三、总结 阅读此需阅读下面这些博客先【Spring源码分析】Bean的元数据和一些Spring的工具【Spring源码分析】BeanFactory系…

【Java程序设计】【C00207】基于(JavaWeb+SSM)的宠物领养管理系统(论文+PPT)

基于&#xff08;JavaWebSSM&#xff09;的宠物领养管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物领养系统 本系统分为前台系统、管理员、收养者和寄养者4个功能模块。 前台系统&#xff1a;游客打开系统…

机器学习入门-----sklearn

机器学习基础了解 概念 机器学习是人工智能的一个实现途径 深度学习是机器学习的一个方法发展而来 定义:从数据中自动分析获得模型,并利用模型对特征数据【数据集:特征值+目标值构成】进行预测 算法 数据集的目标值是类别的话叫做分类问题;目标值是连续的数值的话叫做回…

FullStack之Django(1)开发环境配置

FullStack之Django(1)开发环境配置 author: Once Day date&#xff1a;2022年2月11日/2024年1月27日 漫漫长路&#xff0c;才刚刚开始… 全系列文档请查看专栏: FullStack开发_Once_day的博客-CSDN博客Django开发_Once_day的博客-CSDN博客 具体参考文档: The web framewor…

Docker中配置MySql环境

目录 一、简单安装 1. 首先从Docker Hub中拉取镜像 2. 启动尝试创建MySQL容器&#xff0c;并设置挂载卷。 3. 查看mysql8这个容器是否启动成功 4. 如果已经成功启动&#xff0c;进入容器中简单测试 4.1 进入容器 4.2 登录mysql中 4.3 进行简单添加查找测试 二、主从复…

C++函数分文件编写之VScode版

VScode实现函数的分文件编写 1.下载插件创建项目2.分文件编写内容3.修改主函数文件名 我在分享内容时经常用的软件是VScode&#xff0c;相信有些内存敏感或需要VScode便利性的小伙伴也是更愿意使用VScode。那么接下来我们就盘一盘怎样使用VScode实现分文件编写。 1.下载插件创建…

20240202在WIN10下使用fast whisper缺少cudnn_ops_infer64_8.dll

20240202在WIN10下使用fast whisper缺少cudnn_ops_infer64_8.dll 2024/2/2 10:48 https://blog.csdn.net/feinifi/article/details/132548556 Could not locate cudnn_ops_infer64_8.dll. Please make sure it is in your library path!解决办法 安装cuDNN c:\faster-whisper-…

用HTML5 + JavaScript实现下雪效果

用HTML5 JavaScript实现下雪效果 下面是用HTML5 JavaScript实现下雪效果示例&#xff0c;展示了如何使用 HTML5 的 <canvas> 元素以及 JavaScript 来创建下雪效果。效果如下&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html lang"en">…

【Vue3】源码探索之旅:compiler-core之parseChildren函数(二)

简言 parseChildren函数是在baseParse函数中作为createRoot函数的子节点参数传入的&#xff0c;今天来探索下parseChildren函数。 parseChildren在 compiler-core/src/parse.ts文件内。 parseChildren 这个函数就是用来解析模板字符串内容的 &#xff0c;里面有个while循环…

vue全家桶之状态管理Vuex

一、认识应用状态管理 1.什么是状态管理 在开发中&#xff0c;我们会的应用程序需要处理各种各样的数据&#xff0c;这些数据需要保存在我们应用程序中的某一个位置&#xff0c;对于这些数据的管理我们就称之为是 状态管理。 在前面我们是如何管理自己的状态呢&#xff1f; …

HiSilicon352 android9.0 开机视频调试分析

一&#xff0c;开机视频概念 开机广告是在系统开机后实现播放视频功能。 海思Android解决方案在原生Android基础上&#xff0c;增加了开机视频模块&#xff0c;可在开机过程中播放视频文件&#xff0c;使用户更好的体验系统开机过程。 二&#xff0c;模块结构 1. 海思自研开机…

【Docker篇】Linux安装Docker、docker安装mysql、redis、rabbitmq

1.Linux安装docker 官方帮助文档&#xff1a;Install Docker Engine on CentOS | Docker Docs 1.1安装命令 # 1. 卸载之前的dockersudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate…

认识 SYN Flood 攻击

文章目录 1.什么是 SYN Flood 攻击&#xff1f;2.半连接与全连接队列3.如何防范 SYN Flood 攻击&#xff1f;增大半连接队列开启 SYN Cookie减少 SYNACK 重传次数 参考文献 1.什么是 SYN Flood 攻击&#xff1f; SYN Flood 是互联网上最原始、最经典的 DDoS&#xff08;Distri…

dvwa,xss反射型lowmedium

xss&#xff0c;反射型&#xff0c;low&&medium low发现xss本地搭建实操 medium作为初学者的我第一次接触比较浅的绕过思路high low 发现xss 本关无过滤 <script>alert(/xss/)</script> //或 <script>confirm(/xss/)</script> //或 <scr…

2024数学建模美赛F题思路代码分享

非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估 计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数 据驱动的5年项目&#xff0c;旨在显著减少非法野生动物贸易。你的目标是说服一…

【24美赛思路已出】2024年美赛A~F题解题思路已出 | 无偿自提

A题&#xff1a;资源可用性和性别比例 问题一&#xff1a; 涉及当灯鱼种群的性别比例发生变化时&#xff0c;对更大的生态系统产生的影响。为了分析这个问题&#xff0c;可以采用以下的数学建模思路&#xff1a;建立灯鱼种群模型&#xff1a; 首先&#xff0c;建立一个灯鱼种群…

C语言第十六弹---操作符(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 操作符 1、下标访问[]、函数调用() 1.1、[ ] 下标引用操作符 1.2、函数调用操作符 2、结构成员访问操作符 2.1、结构体 2.1.1、结构的声明 2.1.2、结构体变…

计算机网络第4章(网络层)

4.1、网络层概述 简介 网络层的主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 这些异构型网络N1~N7如果只是需要各自内部通信&#xff0c;他们只要实现各自的物理层和数据链路层即可 但是如果要将这些异构型网络互连起来&#xff0c;形成一个更大的互…

【Python笔记-设计模式】建造者模式

一、说明 又称生成器&#xff0c;是一种创建型设计模式&#xff0c;使其能够分步骤创建复杂对象。允许使用相同的创建代码生成不同类型和形式的对象。 (一) 解决问题 对象的创建问题&#xff1a;当一个对象的构建过程复杂&#xff0c;且部分构建过程相互独立时&#xff0c;可…