初阶指针---从入门到入坟

news2024/10/7 4:30:29

今天我们来见识一下c语言里让万千少年少女从入门到入坟的一道大门槛——指针

目录

1.指针是什么?

 2.指针和指针类型

3.野指针

4. 指针运算

5. 指针和数组

6. 二级指针

7. 指针数组


1.指针是什么?

指针理解的2个要点:

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语中说的指针通常指的是指针变量。

 我们知道内存是电脑上的存储设备,它有4G、8G、16G等等,我们的程序在运行时会加载到内存里,也会使用内存空间。

在内存里,一个内存单元的大小是一个字节, 而每一个内存单元都对应一个编号,这个编号就叫做地址,而地址又有一种叫法叫做指针。

我们写一个程序,比如int a = 5;然后我们&a

 此时,&a就对应着0x0012ff40,我们把&a放入到一个变量里

int* pa = &a;

pa是用来存放地址的,pa就被叫做指针变量,因为a是int类型,所以pa就是int*类型,这个*就告诉我们pa的类型是指针,我们可以写个程序来看看

 那么指针变量怎么使用呢?指针变量的使用就是对他进行解引用操作,比如:

 地址是如何编址的呢?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0); 那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

...

11111111 11111111 11111111 11111111

这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。

同样的方法,我们可以计算出64位机器的,他有64根地址线,这里就不再计算。

 此时我们就可以得出如下结论:

在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。

在64位机器上,如果有64个地址线,一个指针变量的大小是8个字节,才能存放一个地址。

 总结一下就是:指针变量是用来存放地址的,地址是唯一标示一个内存单元的。

                           指针的大小在32位平台是4个字节,在64位平台是8个字节。

 2.指针和指针类型

我们知道变量有多种类型,比如int,float,double,char等等,那么指针有没有类型呢?有没有一种通用类型的指针,叫做ptr,可以用来存放所有的指针呢?答案是没有,指针既然有不同的类型,那就肯定有它自己存在的道理。

我们来看一段代码

 我们可以看到,我们对pa解引用,将a的值改为0,在内存里a的值也确实变为了0。那我们用一个char*类型的指针来存放a的地址,会发生什么呢?

 我们发现,a的值里的44变为了00,这是为什么呢?首先我们要知道,地址是以16进制展示的,但数据存储是2进制的,一个16进制位是4个二进制位,而8个二进制位占一个字节,所以a里边的44占用一个字节,33占用一个字节,22,11也同样占用一个字节,而char类型占用1个字节,我们发现在对pc解引用时只改变了一个字节,也就是44变为了00,而int*类型的指针解引用时访问了4个字节。

我们发现:

指针类型其实是有意义的

1. 指针类型决定了,指针进行解引用操作的时候,一次性访问几个字节,访问权限的大小

如果是char*的指针,解引用访问1个字节

如果是int*的指针,解引用访问4个字节

float* ----------------- 4个字节

2. 指针类型决定指针的步长(指针+1到底跳过几个字节)

字符指针+1,跳过1个字节

整型指针+1,跳过4个字节

 对于上面的代码,有以下解释:

 如果我们想把a完全变为0,我们可以使用for循环循环4次,即可把4个字节全变为0。其他类型的指针以此类推。

指针类型的意义:指针类型的不同,提供了不同视角去观看和访问指针,比如char*类型指针一次访问一个字节,+1跳过一个字节,int*指针一次访问4个字节,+1跳过4个字节。

总结:指针+-整数

对于指针加减整数,我们看个例子,比如:int* p; p+4对应着sizeof(int)*4对应16个字节

          指针的解引用

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

3.野指针

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

野指针的非常危险的,野指针的形成有三种原因:

1. 指针未初始化 

2. 指针越界访问

3. 指针指向的空间释放。

要想规避野指针,我们可以

1. 指针初始化

2. 小心指针越界

3. 指针指向空间释放,及时置NULL

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

如果我们创建一个指针不知道它该指向哪里,那就让它指向NULL,NULL的本质就是0 ,是专门用来初始化指针的。

4. 指针运算

4.1  指针+-整数

我们看这样一段代码

数组名是首元素地址,我们让p等于数组首元素地址,然后通过*p(解引用)的方法将数组元素初始化为1, 然后我们再用解引用的方法打印数组的元素

在内存里是这样的:

 4.2 指针-指针

我们看这样一段代码

我们先看看它们在内存是怎么画的

 

 指针相减的绝对值,是两个指针之间元素的数量,如果上面代码是&arr[4]-&arr[0],那么结果就是4,ps:这里大家可能会疑惑为什么arr[5]可以使用,这是因为:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。也就是说,我们可以写出与arr[5]比较的代码,而不能写与arr[-1]比较的代码,注意,这里是比较大小,不是访问,我们仍然不能越界访问,有的编译器可能对于比较arr[-1]不会报错,但是这是错误的,因为不是所有的编译器都可以,c语言标准只规定可以与arr[5]这样的代码进行比较。

知道了指针-指针的规则,我们就可以写一个strlen函数(之前我们使用过循环和递归解决)

int my_strlen(char *s)
{
       char *p = s;
       while(*p != '\0' )
              p++;
       return p-s;
}

5. 指针和数组

我们先看这样一段代码

 可见数组名和数组首元素的地址是一样的。数组名是首元素地址(两种情况除外,我之前有过详细讲解,大家感兴趣可以看一看)

(2条消息) 数组传参究竟是怎么一回事?_KLZUQ的博客-CSDN博客_传参 数组

1.指针和数组是不同的对象

指针是一种变量,用来存放地址,大小为4/8字节

数组是一组相同类型元素的集合,可以存放多个元素,数组大小取决于数组类型和元素个数 

2.数组的数组名是首元素地址,地址可以放在指针变量中,可以通过指针访问数组

6. 二级指针

概念:指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是二级指针 。

#include <stdio.h>
 
int main() {
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
}

ppa就是一个二级指针,ppa前面的第一个*代表它是一个指针,*前面的int*代表了ppa所指向的类型。

 ppa是指针变量的地址,而不是地址的地址

对于ppa,*ppa访问的是pa,而**ppa访问的是a。

既然有二级指针,同理也就是有三级、四级指针等等,不过我们几乎不使用。

7. 指针数组

指针数组,从名字来看,我们知道有整形数组,浮点型数组,字符数组等等,那么指针数组也是一个数组,只不过里边存放的元素是指针而已。

 那么指针数组有什么用呢?我们来看这样一个例子:

我们可以用指针数组来模仿一个三行三列的数组。

 

在内存里大概就是这样子的,数组arr里存放了a1,a2,a3三个数组的地址,三个地址指向三个数组。这里再解释一下为什么arr[i][j]就可以直接访问数组的元素,比如我们访问a1里的元素,我们使用的是arr[0][j]的方法,arr[0][j]只是展示给我们看的,而编译器会把它转变为*(arr[0]+j)这种形式,与我们之前说过的通过指针访问数组是同一个道理。

以上就是初阶指针的全部内容,希望大家可以有所收获。

如有错误,还请指正。

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

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

相关文章

C语言:计算阶乘与计算从1加到100的代码对比:都要用到3个变量,不同之处在于表达式

计算1 到 100 之间所有整数之和 #include <stdio.h> int main() {int i1,total0;while(i<100)//不能在 while 后面加分号{totali;i;//循环内要有使循环趋近于假的操作}printf("%d\n",total);return 0; } 和下面对比&#xff0c;只不过是100用输入j来代替了 …

将windows的显示器作为linux的扩展屏

这里写自定义目录标题前言WinLinuxRequirementsBuild and install运行前言 测试的linux系统为ubuntu 18.04测试的windows系统为win10将windows的显示器作为linux的扩展屏&#xff0c;需要使用微软的Miracast技术。windows自带就不多说了&#xff0c;linux使用的是这个开源软件…

MATLAB绘图合集:填充二维等高线图contourf

本文主要介绍填充的二维等高线图和基本的用法例子 目录 说明 例子 绘制10个层级的等高线 显示具有标签的特定层级的等高线图 自定义等高线线宽 说明 contourf(Z) 创建一个包含矩阵 Z 的等值线的填充等高线图&#xff0c;其中 Z 包含 x-y 平面上的高度值。MATLAB会自动选择…

【第六部分 | JavaScript高级】3:正则表达式

目录 【第三章】正则表达式&#xff08;重点&#xff09; | 概述 | 创建正则表达式 | 测试某个字符串是否符合正则 | 正则符号 什么是正则符号&#xff1f; 1.1.边界符 ^ $ 1.2.连字符 - 2.1.字符类—方括号符 [] 2.2.字符类—方括号符内 范围符 - &#xff08;易错&a…

nacos服务注册源码过程阅读

准备部分 这是在真正调用注册实例的方法之前&#xff0c;需要使用到的对象的关系图。 源码跟踪 NacosServiceRegistryAutoConfiguration类 Configuration(proxyBeanMethods false) EnableConfigurationProperties ConditionalOnNacosDiscoveryEnabled ConditionalOnPropert…

C语言学习之路(基础篇)—— 内存管理

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 作用域 C语言变量的作用域分为&#xff1a; 代码块作用域(代码块是{}之间的一段代码)函数作用域文件作用域 1) 局部变量 生命周期&#xff1a;…

Python 基础(一):初识 Python

文章目录Python是什么解释型语言Python 之父Python 名字的由来Python 的应用领域人生苦短&#xff0c;我用 Python大家好&#xff0c;我是水滴~~ 本文对 Python 做了一个初步的介绍&#xff0c;并了解 Python 的作者、名字由来、应用领域等。 Python是什么 Python 是一种面向…

什么是软件测试?

什么是软件测试&#xff1f; 软件测试的定义&#xff1a;在一定条件下对软件进行操作&#xff0c;发现软件的问题&#xff0c;提高软件的质量。 软件测试在开发中的有着重要地位。软件测试在各阶段的完成相应的任务&#xff0c;需求测试&#xff0c;架构测试&#xff0c;详细测…

C语言条件运算符——三元表达式例题(素材来自C技能树)

&#x1f4d1;三目运算符 三目运算符也叫条件运算符、三元运算符&#xff0c;是由一个问号和一个冒号组成。语法&#xff1a;表达式1?表达式2:表达式3;语义&#xff1a;先执行表达式1&#xff0c;如果表达式1的结果如果为真&#xff0c;那么执行表达式2&#xff0c;并且这个整…

level2接口有什么用?是如何获取A股行情数据的?

目前国内有很多数据团队专门为金融机构、学术团体和量化研究者们提供的本地量化金融数据服务&#xff0c;那么最常见的就是通达信、同花顺等团队&#xff0c;他们开发出来的level2接口可快速查看和计算金融数据&#xff0c;无障碍解决本地、Web、金融终端调用数据的需求。为了满…

彻底解决 K8s 节点本地存储被撑爆的问题

一、存储的内容 要解决存储使用过多的问题&#xff0c;就得先了解存储中都保存了些什么内容&#xff0c;否则解决不了问题&#xff0c;还可能带来更多的风险。 1.1、镜像 容器要在节点上运行&#xff0c;kubelet 首先要拉取容器镜像到节点本地&#xff0c;然后再根据镜像创建…

3分钟读懂RD与RT

Route-Distinguisher&#xff08;后简称"RD"&#xff09;&#xff0c;Route-Target&#xff08;后简称"RT"&#xff09;经常出现在EVPN、MPLS VPN中&#xff0c;但它们是完全不同的两个概念&#xff0c;初学者往往难以区分两者的差异。学霸题&#xff1a;区…

Jmeter入门

性能测试&#xff1a;模拟多个用户的操作对服务器硬件性能的影响 TPS&#xff1a;Transaction per Second&#xff0c;每秒事务处理能力 RT&#xff1a;Response Time&#xff0c;响应时间 安装 由于本人只有window系统&#xff0c;故只讲解win下的安装 安装JDK 下载地址&a…

2023年最热门的网络安全岗位分析

大数据、人工智能、云计算、物联网、5G等新兴技术的高速发展&#xff0c;蒸蒸日上。但是随之也出现了许多问题&#xff0c;比如&#xff1a;政府单位、企业、个人信息泄露&#xff0c;网络安全问题日益严峻&#xff0c;网络空间安全建设刻不容缓。 网络安全人才需求量巨大&…

双核驱动,合力共进,郁锦香与凯里亚德酒店强强联合释放多元化商业价值

近日&#xff0c;以“清风雅茗 亨嘉之会”为主题的2022锦江酒店&#xff08;中国区&#xff09;厦门站品牌投资品鉴会圆满落幕&#xff0c;众多投资人和酒店品牌方负责人齐聚一堂&#xff0c;在充满文艺气息的滨海城市厦门&#xff0c;感受精致、愉悦的慢生活。在品牌见面环节&…

NodeJs实战-Express构建照片存储网站(1)-ejs视图引擎填充数据

ejs视图引擎填充数据express 生成项目安装 express-generator生成项目程序结构理解项目结构生成的文件的含义视图渲染填充照片数据增加路由器修改 app.js修改 routes增加对应的视图页面路由器 res.render 查找视图逻辑新增文件之后的项目结构图效果图项目地址express 生成项目 …

kali Linux常用快捷键及vim的基本使用

kali Linux 系统快捷键 Ctrl Alt T &#xff1a;打开一个新的命令行终端。 如果是在桌面打开的是这种情况 Ctrl C 复制。 Ctrl Z 撤消。 Ctrl S &#xff1a;保存 Ctrl Q &#xff1a;退出。 终端快捷键 TAB &#xff1a;补全命令。 Ctrl &#xff1a;放大文字…

Netty系列(一):Springboot整合Netty,自定义协议实现

Netty是由JBOSS提供的一个java开源框架&#xff0c;现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说&#xff0c;Netty 是一个基于NIO的客户、服务器端的编程框架&…

目标检测论文解读复现之十六:基于改进YOLOv5的小目标检测算法

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

Java项目使用intellij-IDEA查看依赖包版本是否有冲突(方法及工具)

编译器及版本idea-ultimate依赖管理工具maven 第一个是idea本身的 Step1&#xff1a;点击右侧的maven Step2&#xff1a;右键依赖项&#xff0c;点击分析依赖关系 Step3&#xff1a;可以在模块名位置进行切换&#xff0c;左侧三角的标志则表示该包引入了多个版本&#xff…