[C语言]指针简介

news2026/3/24 20:52:59
前言指针是C语言中的精髓意味着学好指针才能发挥出C语言的强大作用。要看一个程序员用C的能力强不强就要看其对指针的理解到不到位。指针数据存储在内存中。为了高效地访问数据内存中的每个字节都被赋予一个唯一的地址。通过该地址我们可以定位到对应的数据并进行相应操作。取地址操作符、指针变量取地址操作符。用来取出变量存储在内存中的地址指针变量指针变量可以用来存储取出来的地址如下图中的变量p1就是指针变量变量的类型是int*也就是指向整形的指针。inta0;int*pAa;// a 取变量 a 的地址 pA类型为int* 的指针变量pA的意思是point a。return0;指针变量本身也是一个变量说明变量pA本身也占用字节指针变量在32位操作环境下大小为4字节而在64位操作环境下大小则为8个字节。不管指针指向什么数据在同一环境下它存储地址所需要的字节数都是固定的。上述int*是指向整形的指针变量还有各种其他的指针变量如char**和double*以及void*无类型指针通用指针…。解引用操作符得到了数据的存储地址那么要怎么获得指针指向的实际数据呢*解引用操作符解读地址的内容以读取数据或者是修改数据。例如#includecstdiointmain(){charaa;// 创建了一个字符变量 a并将字符的值初始化为 achar*pAa;printf(%c\n,*pA);//}运行结果a指针的加减运算指针变量可以存储的地址也可以进行加减运算那么指针进行加减操作有什么意义呢下面请看一段代码和运行结果示例代码#includestdio.hintmain(){inta0;int*pAa;printf(%d\n,*pA);printf(这是p1的地址: %p\n,pA);printf(这是p11的地址: %p\n,pA1);printf(这是p12的地址: %p\n,pA2);putchar(\n);charba;char*pBb;printf(%c\n,*pB);printf(这是p2的地址: %p\n,pB);printf(这是p21的地址: %p\n,pB1);printf(这是p22的地址: %p\n,pB2);return0;}运行结果0 这是p1的地址: 004FF9DC 这是p11的地址: 004FF9E0 这是p12的地址: 004FF9E4 a 这是p2的地址: 004FF9C7 这是p21的地址: 004FF9C8 这是p22的地址: 004FF9C9在内存中int和char类型分别占用4字节和1字节的内存大小。 当我们对其类型的指针变量进行加减操作时指针会按照其类型的大小进行位移如int*的指针加上1就会从当前地址位移4个字节char*指针加上1就会位移一个字节。如果我们指针p指向的是一个数组的起始元素地址通过数组元素在内存中存储是连续的特性可以通过加和减来定位数组中的每个元素的地址计算偏移量时要小心避免越界。数组名的理解指针可以指向数组的元素下面我们来研究一下指向数组的指针该如何使用吧。下面来看具体例子说明数组变量名代码示例#includecstdiointmain(){intarr[10]{0};// arr 的类型: int[10]printf(%-20s %-14s %-10s\n,变量,地址(16进制),地址10进制);printf(%-20s %-14p %-10d\n,arr:,arr,(int)arr);printf(%-20s %-14p %-10d\n,arr[0]:,arr[0],(int)arr[0]);printf(%-20s %-14p %-10d\n,arr:,arr,(int)arr);printf(%-20s %-14p %-10d\n,arr[0] 1:,arr[0]1,(int)(arr[0]1));printf(%-20s %-14p %-10d\n,arr 1:,arr1,(int)(arr1));printf(-------------------------------------\n);printf(%-20s %-14s\n,变量,大小);printf(%-20s %-14zu\n,sizeof(arr):,sizeof(arr));printf(%-20s %-14zu\n,sizeof(arr[0]):,sizeof(arr[0]));return0;}运行结果变量 地址(16进制) 地址10进制 arr: 006FF7A8 7337896 arr[0]: 006FF7A8 7337896 arr: 006FF7A8 7337896 arr[0] 1: 006FF7AC 7337900 arr 1: 006FF7D0 7337936 ------------------------------------- 变量 大小 sizeof(arr): 40 sizeof(arr[0]): 4运行结果显示数组名其实是一个地址它不仅代表数组首元素的地址还可以代表整个数组。数组名只有在两种情况下才代表整个数组的地址。第一种情况arr这个操作是将整个数组的地址取出来其值是数组的首元素地址观察上面的运行结果中arr、arr1 1和arr 1的十进制地址发现arr1 1相比arr的偏移量是 4一个int变量的大小而arr 1跳相比arr的偏移量是 40一个数组的大小也就是10个int的大小。第二种情况arr1放在sizeof()的括号里头时代表的是整个数组计算的内存是整个数组占用的内存大小。既然数组名是首元素的地址我们可以尝试用指针来访问数组的每一个元素而不用下标操作符“[]”来操作符下面我们来看代码的具体实现。#includecstdiointmain(){intarr[10]{10,9,8,7,6,5,4,3,2,1};int*parr;size_t sizesizeof(arr)/sizeof(arr[0]);// 此表达式可以计算数组中单个元素的大小for(inti0;isize;i){printf(%d ,*(pi));}putchar(\n);for(inti0;i10;i){printf(%d ,p[i]);}return0;}运行结果10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1想要获取数组中的任意一个元素可以用*(p i)的方式或p[i]的方式获取。换句话说*(p1i)和p1[i]是同一个意思。循环里变量i每增加一次我们的指针就指向下一个元素的起始位置然后根据指针类型进行解引用操作就可以访问数组的每个元素。指针数组指针数组就是指针的数组这个数组存放的变量是指针变量。我们通过指针数组模拟二维数组的示例代码来理解指针数组。示例代码#includecstdio//指针数组模拟实现二维数组intmain(){//初始化三个一维数组intarr1[5]{1,2,3,4,5};intarr2[5]{6,7,8,9,10};intarr3[5]{11,12,13,14,15};//指针数组//arr数组存放的变量类型是int*的指针//存放的是一维数组的首元素地址int*arr[3]{arr1,arr2,arr3};//遍历数组模拟一个三行五列的二维数组for(inti0;i3;i){for(intj0;j5;j){printf(%2d ,*(arr[i]j));}//arr[i]可写成*(arri)printf(\n);}return0;}运行结果1 2 3 4 5 6 7 8 9 10 11 12 13 14 15我们所创建的指针数组里三个元素存放的是指针变量每个指针变量指向三个一维数组的起始位置当我们读取数组中第一个元素其实是得到第一个一维数组的首元素地址我们对其 i指针偏移并进行解引用就可以访问到每一个元素了。我们可以将上面代码的for循环中的i5条件改为i20,这样做虽然是不允许的但是为了深入探讨问题我们可以试一试。修改的代码for(intj0;j20;j)// 循环条件 j 5 改为 j 20{printf(%2d ,*(arr[i]j));}运行结果1 2 3 4 5 -858993460 230655617 13630200 6234595 1 17117464 17120824 1 17117464 17120824 13630292 6234154 230655757 6230051 6230051 6 7 8 9 10 -858993460 -858993460 1 2 3 4 5 -858993460 230655617 13630200 6234595 1 17117464 17120824 1 11 12 13 14 15 -858993460 -858993460 6 7 8 9 10 -858993460 -858993460 1 2 3 4 5 -858993460我们会发现访问完一维数组中的五个元素后后面访问到的元素中有莫名其妙的值也有访问完的上一个一维数组存储的值。这是使用指针数组模拟二维数组与直接开辟二维数组的区别。开辟空间时是依据从高地址向低地址的顺序开辟的当我们继续向高地址进行访问时就有可能访问到之前开辟的一维数组的元素。直接创建的二维数组在内存中是连续的而我们用指针数组和一维数组模拟的二维数组在内存中并不是连续的。字符指针我们来看一段代码#includecstdio// 字符指针和数组指针intmain(){charstr1[20]{I love you!};printf(%s\n,str1);constchar*str2I love you!;printf(%s\n,str2);return0;}看到这里你是不是以为第二种方式是将字符串“I love you”传入了字符指针里了其实它是将字符串中首元素地址“I”传入了str2中只要找到字符串数组的首元素地址就可以将字符串打印出来了\0为字符串的结束标志。数组指针数组指针就是指针指向的内容是一个数组(细品这句话)。下面我们用二维数组传参的方式来认识一下数组指针。#includecstdiointmain(){//数组指针 指向数组intarr[3][5]{{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};int(*pArr)[5]arr;//这里放入的是二维数组首元素即一维数组的地址for(inti0;i3;i){for(intj0;j5;j){// pArr指向的是一个一维数组// (pArr i) 理解为指向数组指针中的第 i 个一维数组类型为 (*pArr)[5]// *(pArr i) 解引用第 i 个一维数组指向一维数组的首元素类型为 int*// *(pArr i) j) 理解为指向一维数组中的第 j 个元素的起始位置类型为 int*// *(*(pArr i) j) 解引用第 j 个元素的起始位置获取到 int 元素printf(%2d ,*(*(pArri)j));}putchar(\n);}printf(-----------------等价的访问方式-----------------\n);for(inti0;i3;i){for(intj0;j5;j){// *(*(parr1i)j)printf(%2d ,pArr[i][j]);}putchar(\n);}return0;}运行结果1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -----------------等价的访问方式----------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15代码中我们声明了一个数组指针即int (*pArr)[5]。因为下标操作符[]比解引用操作符优先级更高防止被理解为int*类型的指针数组所以我们需要给指针*pArr加上括号来保证其所代表的是指针而不是数组。指针pArr指向的是int [5]类型的数组。所谓的二维数组我们可以理解为一个存储了一维数组的数组也就是二维数组中每个元素即为一个一维数组所以二维数组的数组名代表了第一个整个一维数组的地址即二维数组名指向二维数组的第一行元素一个一维数组的起始地址.我们将二维数组的第一个元素的地址存入数组指针pArr中当我们进行pArr i操作时数组指针就会指向二维数组的下一个元素下一个一维数组。当我们对这一个元素的地址进行解引用的时候我们就得到了这一个元素中的第一个元素该元素就是int的地址元素的元素降维此时的*pArr i指向的就是一维数组中的首个int元素的地址当我们再进行 j操作时指针就会往后移动j个元素解引用后得到第这个一维数组的第j个元素。使用这样的方法这样我们就能用数组指针遍历二维数组里每一个元素了并将其打印出来。函数指针基本用法函数指针也就是指向函数的指针我们可以通过函数指针来调用函数。函数由函数名、参数、返回值组成其中函数名不仅仅是函数名还代表着函数的地址。我们可以通过函数名和函数名的方法得到函数的地址这两种方法都是一样的没有什么区别。下面我们通过代码来看看函数指针是如何声明和使用的吧示例代码#includecstdio//函数指针intmax(inta,intb){returnab?a:b;//返回a和b中的最大值}intmain(){int(*p)(int,int)max;// 函数指针inta3,b4;printf(%d和%d的最大值是max(a, b) %d。\n,a,b,max(a,b));printf(%d和%d的最大值是p(a, b) %d。\n,a,b,p(a,b));return0;}运行结果3和4的最大值是max(a, b) 4。 3和4的最大值是p(a, b) 4。在上述代码中我们声明了一个函数指针p它指向的函数类型的返回值是int型传入的形参是两个int的数据int, int)。在后面的代码中我们用指针的方式调用了函数max来求出两个数的最大值即max(a, b)。如果不用常规函数名方式调用函数也可以用函数指针也就是p(a, b)。容易观察到函数名起始就是一个地址我们将函数地址存入函数指针中我们就可以通过函数指针的方式来调用该函数了。结合 qsort 排序任意类型的数组函数指针的一个用法c 标准库中有一个快速排序的函数叫做qsort如果想要对自定义类型的数组如自己定义的结构体的数组进行排序那么就要告诉qsort排序规则。首先要自己定义一个函数根据比较方法来返回比较的结果然后把这个比较规则的函数的函数指针传给qsort供其内部调用这样就可以实现对任意类型的数据进行排序了代码示例#includestdio.h#includestdlib.h#includestring.h// 定义学生结构体typedefstruct{charname[20];intage;floatscore;}Student;// 按年龄升序排序intcompareByAge(constvoid*a,constvoid*b){Student*s1(Student*)a;Student*s2(Student*)b;returns1-age-s2-age;// 升序}// 按分数降序排序intcompareByScoreDesc(constvoid*a,constvoid*b){Student*s1(Student*)a;Student*s2(Student*)b;// 分数高的排前面降序if(s1-scores2-score)return-1;if(s1-scores2-score)return1;return0;}// 按姓名升序排序intcompareByName(constvoid*a,constvoid*b){Student*s1(Student*)a;Student*s2(Student*)b;returnstrcmp(s1-name,s2-name);// 字符串比较}// 打印学生信息voidprintStudents(Student*students,intn){for(inti0;in;i){printf(%-10s\t%d岁\t%.1f分\n,students[i].name,students[i].age,students[i].score);}}intmain(){// 初始化学生数组Student students[]{{Max Caulfield,20,85.5},{Chloe Price,18,92.0},{Rachel Amber,22,78.5},{Kate Marsh,19,88.0},{Warren Graham,21,95.5}};intnsizeof(students)/sizeof(students[0]);printf( 原始数据 \n);printStudents(students,n);// 按年龄排序qsort(students,n,sizeof(Student),compareByAge);printf(\n 按年龄升序 \n);printStudents(students,n);// 按分数降序排序qsort(students,n,sizeof(Student),compareByScoreDesc);printf(\n 按分数降序 \n);printStudents(students,n);// 按姓名排序qsort(students,n,sizeof(Student),compareByName);printf(\n 按姓名升序 \n);printStudents(students,n);return0;}运行结果 原始数据 Max Caulfield 20岁 85.5分 Chloe Price 18岁 92.0分 Rachel Amber 22岁 78.5分 Kate Marsh 19岁 88.0分 Warren Graham 21岁 95.5分 按年龄升序 Chloe Price 18岁 92.0分 Kate Marsh 19岁 88.0分 Max Caulfield 20岁 85.5分 Warren Graham 21岁 95.5分 Rachel Amber 22岁 78.5分 按分数降序 Warren Graham 21岁 95.5分 Chloe Price 18岁 92.0分 Kate Marsh 19岁 88.0分 Max Caulfield 20岁 85.5分 Rachel Amber 22岁 78.5分 按姓名升序 Chloe Price 18岁 92.0分 Kate Marsh 19岁 88.0分 Max Caulfield 20岁 85.5分 Rachel Amber 22岁 78.5分 Warren Graham 21岁 95.5分转移表我们可以创建一个指针数组用来存放我们的函数的地址然后通过不同的条件调用数组中的不同函数这种方式的调用就叫转移表。如下是简单的创建函数指针数组实现简易的转移表代码示例代码#define_CRT_SECURE_NO_WARNINGS1#includecstdiointmax(inta,intb){returnab?a:b;// 返回a和b中的最大值}intmin(inta,intb){returnab?a:b;// 返回a和b中的最小值}intmain(){int(*p[3])(int,int){NULL,max,min};inta,b,choice;do{printf(请输入两个整数: \n);scanf(%d %d,a,b);printf(请输入你的操作: 1---求最大值 2---求最小值\n);scanf(%d,choice);if(choice1)printf(%d和%d的最大值为: ,a,b);elseif(choice2)printf(%d和%d的最小值为: ,a,b);else{printf(错误请输入正确的操作\n);return-1;}printf(%d\n,p[choice](a,b));printf(是否继续? 继续---Y, 退出---N\n);getchar();// 用于接收换行符}while(Ygetchar());return0;}运行结果请输入两个整数: 520 1314 请输入你的操作: 1---求最大值 2---求最小值 1 520和1314的最大值为: 1314 是否继续? 继续---Y, 退出---N Y 请输入两个整数: 520 1314 请输入你的操作: 1---求最大值 2---求最小值 2 520和1314的最小值为: 520 是否继续? 继续---Y, 退出---N N以上就是我对于指针的理解

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…