浅析C指针

文章目录

  • 前言
  • 指向整型变量的指针
  • 指向一维数组的指针
    • 以指针为形参且以指针为返回值类型
    • 以局部变量地址作为返回值存在的安全隐患
  • 指向字符串的指针
  • 通过指针交换两变量的值
  • 多级指针
  • 指针数组
  • 数组指针
  • 指向函数的指针


前言

C语言中的指针实质是指向某一对象的内存地址,对象可以是变量、常量、数组、函数等。不同类型的指针大小是不变的,例如在64位操作系统中,指针大小占8个字节。可以对地址进行前移、后移运算;可以通过运算符*获取内存地址保存的数据。

操作系统大小(字节)备注
62位8int*、float*、char*等指针大小均为8字节
32位4int*、float*、char*等指针大小均为4字节

指向整型变量的指针

  • 建立一个整型变量num,假设其地址为1000,并在其内存空间中保存数据10
  • 建立一个整型变量temporary,假设其地址为1020,并在其内存空间中保存数据20
  • 建立一个整型指针变量p,指向num的地址,也就是地址1000,此时p和num指向同一个内存地址
  • 将指针p指向的地址中保存的数据更改为temporary的数据,即修改为20,因为p和num指向同一个内存地址,故*p和num的值均被更改为20
    以此可以推导出其他基本数据类型指针的简单运用
/// Description 指向整形变量的指针
void varPrime(){int num = 10,temporary = 20;//一个整形指针变量指向了一个整形变量的地址int *p = #//修改p指向的地址中的数据内容*p = temporary;printf("%d",num);
}

指向一维数组的指针

  • array是一个一维整型数据,代表首元素首地址,也就是&array[0]
  • 整型指针变量p指向array的首元素首地址,假设首元素首地址为1000
  • p++;即代表地址后移(1000+(1*4))=1004,因为int型在64位操作系统中占4个字节,故每次进行地址移动,以4位倍数,执行完p++;后,p当前指向数组内第二个元素array[1]的地址
  • p–;即代表地址前移(1004-(1*4))=1000(因为上一次执行完之后p指向的地址是1004,故从此地址开始计算),执行完p–;后,p当前指向数组内第一个元素array[0]的地址
  • *(p+i):因为一维数组采用顺序存储,拥有随机访问的特点,因为指针指向的数组也可以通过随机访问进行读取内存空间上的数据
/// Description 指向一维数组的指针
void oneArrayPrime(){int array[] = {10,20,30};//因为array代表的是数组的首元素地址,故前面不需要加&地址符号int *p = array;//数组下标后移,初始化指向第一个元素10,后移操作之后指向20p++;//数组下标前移,继上一个操作之后,指针指向元素10,前移操作之后指向10p--;//遍历数组,输出数组所有内容for(int i=0; i<3; i++){printf("current element: %d\n",*(p+i));}
}

以指针为形参且以指针为返回值类型

通过在一个数组内寻找最大值的例子,概述以指针为形参且以指针为返回值类型

  • 实参中传入一维数组的首元素首地址,形参则使用一个整型指针来进行传递,此指针指向一维数组的首元素首地址
  • 然后在通过地址符号&返回最大值的地址,同样,因为是地址,所以返回值类型需要变为指针来进行传递
  • 此处以局部变量地址作为返回值,存在一定安全隐患,将在下一节讲述
/// Description 数组作为函数实参,指针作为函数形参寻找数组内最大值,并以指针作为返回值类型
/// - Parameters:
///   - array: 整形指针变量,指向一维数组
///   - size: 数组长度
int* findMax(int *array,int length){int max = *array;for(int i=1; i<length; i++){if(*(array+i) > max) max = *(array+i);}return &max;
}void testMax(){int array[] = {1,2,3,4};int length = sizeof(array) / sizeof(int);int *max = findMax(array, length);printf("max=%d\n",*max);
}

以局部变量地址作为返回值存在的安全隐患

  • List item上述寻找最大值例子以局部变量地址作为返回值进行返回,但上述例子可以正常运行,而下列例子则存在安全隐患。
  • 因为当函数执行完成之后,系统会对此函数进行回收,其局部变量也被回收,但系统会留有一个时机,上述例子抓住时机,并将其进行输出。
  • 而下列例子在中间穿插了一句代码,导致错过时机,指针变量p变为野指针,指向一个系统随机分配的地址。
  • 一般会保留一个语句的执行频度(各编译器存在差异),也就是说当下一个语句执行完后,系统会对其进行释放
int* returnPrime(){int n = 10;return &n;
}/// Description 接受来自另一个函数的局部变量返回值
/// 当函数执行完成后,系统不会立即回收此地址
///一般会保留一个语句的执行时间(各编译器存在差异),也就是说当下一个语句执行完后,系统会对其进行释放
///所以p指向的地址被系统释放,故随后指向一个随机的地址
void testReturnPrime(){int *p = returnPrime();printf("split line!\n");printf("n=%d\n",*p);
}

指向字符串的指针

字符串不等同于字符数组

类型举例长度区别
字符数组char str1[] = {‘a’,‘b’,‘c’};3系统不会默认在结尾追加结尾符'\0'
字符串char str2[] = “abc”;4系统会默认在结尾追加结尾符'\0'

字符指针p指向字符串str首元素首地址,因为str代表的就是首元素首地址,本身就是一个地址,所以在赋值给字符指针p时,无须加地址符号&,此处区别于上述指针整型变量的指针

/// Description 指向字符串的指针
void stringPrime(){//字符串,系统默认在其最后添加结尾符'\0'char str[] = "Hello World!";char tempStr[] = "C Prime!";//字符指针变量p指向字符串首元素首地址char *p = str;int size = strlen(p);printf("The array length:%d\n",size);//输出方式一:直接使用字符串方式输出,因为数组str占据着一连串连续的地址空间printf("%s\n",p);//输出方式二:通过类似数组str[i]的方式逐个访问地址然后输出for(int i=0; i<size; i++){printf("%c",*(p+i));}putchar('\n');//输出方式三:通过指针后移操作,改变指向的地址,依次读取输出for(;*p!='\0';p++){printf("%c",*p);}putchar('\n');//更改字符指针指向的地址p = tempStr;printf("%s\n",p);putchar('\n');
}

通过指针交换两变量的值

  • *a = *b:这一句为关键,代表着将b内存地址上的内容赋值到a内存地址上,则将原本a的内容进行覆盖

如果将上述第三行代码改为a=b;取消两端的指针*运算
则形参a指向b的地址,但由于未使用指针进行运算,形参a的内容只保留在局部范围之内,不改变实参数a的内容
故输出a=1,b=1

/// Description 通过将整形指针变量作文形参,交换两个变量的值
/// - Parameters:
///   - a: 第一个指针变量
///   - b: 另一个指针变量
void swap(int *a,int *b){int temp;temp = *a;*a = *b;*b = temp;//如果将上述第三行代码改为a=b;取消两端的指针取值操作//则形参a指向b的地址,但由于未使用指针进行运算,形参a的内容只保留在局部范围之内,不改变实参数a的内容//故输出a=1,b=1
}
void testSwap(){int a=1,b=2;printf("before swap:a=%d,b=%d\n",a,b);swap(&a, &b);printf("after swap:a=%d,b=%d\n",a,b);
}

多级指针

  • 一级整型指针变量指向a的地址
  • 二级指针指向一级指针的地址;依次类推
  • 可以换一种思维,把二级指针看作一级指针,一级指针看作一个变量,各“向下降一级",就回到一级整型指针变量指向变量a的地址
  • ***p3:等同于*(*(*p3)),从括弧内往外剖析

最里面的(p3)取出的是p2的地址
然后第二层就为(p2)取出来的是p1的地址
第一层就为(p1)的值
p3取的是变量a的值

void multPrime(){int a = 10;//一级整型指针变量指向a的地址int *p1 = &a;//二级指针指向一级指针的地址,//可以换一种思维,把二级指针看作一级指针,一级指针看作一个变量,各“向下降一级",就回到第一条语句思想int **p2 = &p1;//***p3可以看作是*(*(*p3))//从内往外剖析//最里面的(*p3)取出的是p2的地址//然后第二层就为(*p2)取出来的是p1的地址//第一层就为(*p1)的值int ***p3 = &p2;printf("p1=%d\n",*p1);printf("p2=%d\n",**p2);printf("p3=%d\n",*(*(*p3)));
}

指针数组

定义:指针数组:包含n个元素,其中每一个元素都是指针

例如: int * p[n]; 其中[]的运算优先级大于*,p为数组,其类型为int*,则数组内每个元素类型皆为int*

  • 下列指针数组array中保存p1,p2,p3三个整型指针变量
  • 同样,因为是数组具有随机访问的特性;
  • **(array+i)等同于*(*(array+i))
  • 其中*(array+i)获取是第i个元素,因为元素是指针,是指向的一个内存地址,故*(array+i)取出的是pi的地址
  • 再在外面加一个取值*符号,就是对*pi等于*(*(array+i))进行取值运算,获取其内存空间的数据
/// Description 指针数组:包含n个元素,其中每一个元素都是指针
/// 例如: int * p[n];
/// 其中[]的运算优先级大于*,p为数组,其类型为int*,则数组内每个元素类型皆为int*
void primeArray(){int a = 10,b=20,c=30;int *p1=&a,*p2=&b,*p3=&c;//定义一个整型指针数组,包含3个元素,其中每一个元素都是整形指针int *array[] = {p1,p2,p3};//*指针在32位操作系统占4个字节,在64位操作系统占8个字节,//与其类型无关,int*,char*,float*...在64位操作系统都占占8个字节int length = sizeof(array) / sizeof(int*);for(int i=0; i<length; i++){printf("%d ",**(array+i));}}

数组指针

定义:数组指针:指向数组的指针

例如:int (*p)[n];
()的优先级大于[],p为指针,指向n个一维数组
此处的n必须与二维数组的列数保持一致(以二维为例)

  • int (*q)[3] = b;数组指针p指向一个2行3列的二维数组的首元素首地址
  • *(*(q+i)+j),其中*(q+i)先获取二维数组的地址,*(q+i)+j获取b[i][j]的地址,最后在外面使用*取值运算,即获取b[i][j]的值
/// Description 数组指针:指向数组的指针
/// 例如:int (*p)[n];
/// ()的优先级大于[],p为指针,指向n个一维数组
void Arrayprime(){int a[5] = {1,2,3,4,5};int (*p)[5] = &a;for(int i=0; i<5; i++){//因为a是一维的,只有一行,所以下列语句等同于p[0][i]printf("%d ",*(*p+i));}putchar('\n');int b[2][3] = {{1,2,3},{4,5,6}};int (*q)[3] = b;for (int i=0; i<2; i++) {for (int j=0; j<3; j++) {printf("%d ",*(*(q+i)+j));}putchar('\n');}
}

指向函数的指针

此处以寻找最小值为例

int (*p)(int,int) = findMin;
从左到右结合,第一个(*p)大于第二个(),故p为一个指针,指向一个函数

  • 函数指针p的函数参数定义必须和所指向函数的参数类型、个数、顺序保持一致
  • 函数指针中的函数参数定义可以省略具体的参数名
int findMin(int a,int b){return a<b? a : b;
}/// Description 函数指针:顾名思义,指向函数的指针
void functionPrime(){int a=1,b=2;//其中函数指针指向函数定义对应的参数名称可省略//从左到右结合,第一个(*p)大于第二个(),故p为一个指针,指向一个函数int (*p)(int,int) = findMin;int min = (*p)(a,b);printf("min=%d\n",min);
}

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

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

相关文章

mysql的group by与order by同时使用导致排序

场景描述&#xff1a;接手离职同事的一行代码&#xff0c;如下&#xff1a; select pdtm.name,pdtm.temperature,pdtm.humidity,pdtm.pressure from ( select pp.id,p.name,pp.temperature,pp.humidity,pp.pressure from phe_device_thermo_mst p LEFT JOIN phe_device_thermo…

keepalived

目录 keepalived概述 vrrp工作原理 keepalived体系主要的模块及其作用 模块 core模块 vrrp模块 check模块 作用 keepalived工作原理 keepalived和lvs-DR实验 keepalived概述 keepalived高可用应用&#xff08;健康检查&#xff0c;故障切换&#xff09; 节点服务器…

Spring AOP教程_编程入门自学教程_菜鸟教程-免费教程分享

教程简介 AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续&#xff0c;是软件开发中的一个热点&#xff0c;也是Spring框架中的一个重要内容…

量子非凡暴风去广告接口

>>>https://videos.centos.chat/lzffbf.php/?url 免费提供综合去广告接口&#xff0c;各位请友好调用

Android 使用模拟器模拟Linux操作系统

1. 简介 在Android手机上使用模拟器模拟ubuntu等操作系统&#xff0c;便于测试 2. 软件准备 Termux&#xff1a;是一款 Android 终端模拟器和 Linux 环境应用程序&#xff0c;无需 root 或设置即可直接运行。虽然酷安和谷歌菜市场都能下载&#xff0c;但这些渠道都很久没更新…

集成学习:Bagging, Boosting,Stacking

目录 集成学习 一、bagging 二、boosting Bagging VS Boosting 1.1 集成学习是什么&#xff1f; Bagging Boosting Stacking 总结 集成学习 好比人做出一个决策时&#xff0c;会从不同方面&#xff0c;不同角度&#xff0c;不同层次去思考&#xff08;多个自我&am…

unity使用vs和手机联调

1.在打包的时候&#xff0c;选择develop build&#xff0c;并且根据需要勾选log&#xff0c;deep profiler等选项 2.将手机打开usb调试模式&#xff0c;连接电脑 3.手机上安装这个测试包 4.点击测试包运行&#xff0c;然后这时候会弹出来一个端口的提示确认框 5.这时候点击Cons…

保姆级别0-10级完整的在线企业帮助中心、官网博客搭建教程及工具

在今天的数字时代&#xff0c;拥有一个完善的在线企业帮助中心和官网博客是非常重要的。这些平台不仅可以提供有关产品和服务的信息&#xff0c;还可以增加客户互动&#xff0c;建立品牌声誉。在这个教程中&#xff0c;我们将从零开始创建一个帮助中心和官网博客&#xff0c;包…

Config:服务端连接Git配置

创建子模块 Pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org…

飞天使-k8s基础组件分析-安全

文章目录 名称空间解释访问kubernetes API的控制RBAC的介绍 kubeconfig用户的创建集群默认角色 给组创建授权针对pod配置服务账户参考文档 名称空间解释 名字是啥&#xff1f; 答&#xff1a;集群中每个对象的名称对于该类型的资源都是唯一的。并且每一个对象在整个集群中也有…

微服务基础知识

文章目录 微服务基础知识一、系统架构的演变1、单体应用架构2、垂直应用架构3、分布式SOA架构&#xff08;1&#xff09;什么是SOA&#xff08;2&#xff09;SOA架构 4、微服务架构5、SOA和微服务的关系&#xff08;1&#xff09;SOA&#xff08;2&#xff09;微服务架构 二、分…

【QT】progressBar的使用(13)

progressBar多用于记录程序运行的时间、文件下载的时间等等&#xff0c;今天就来看一下&#xff0c;如何熟练运用progressBar。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&#xff1a;Download Python | Python.org 2.QT Designer 官方下载路径&#xff1a;Qt…

java高频面试题(反射、对象拷贝)

java高频面试题&#xff08;反射、对象拷贝&#xff09; 什么是反射&#xff1f; 反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力 Java反射&#xff1a; 在Java运行时环境中&#xff0c;对于任意一个类&#xff0c;能否知道这个类有哪些属性和方法&#…

数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补

数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补 目录 数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补实践过程基本介绍研究背景程序设计参考资料实践过程 基本介绍 数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填…

redis五大类型分析--list(2)

此篇为对redis五大数据类型中list的分析&#xff0c;希望能有所帮助 List API listTypeGet函数 robj *listTypeGet(listTypeEntry *entry) {robj *value NULL;/* 检查编码类型是否为 quicklist (快速列表) */if (entry->li->encoding OBJ_ENCODING_QUICKLIST) {/* 元素…

【LeetCode-中等题】48. 旋转图像

文章目录 题目方法一&#xff1a;使用辅助数组矩阵 行列的规律方法二&#xff1a;原地修改 递推公式 题目 方法一&#xff1a;使用辅助数组矩阵 行列的规律 public void rotate(int[][] matrix) {int n matrix.length;int[][] matrix_new new int[n][n];for(int i 0 ; i<…

深入浅出AXI协议(2)——通道及信号

一、前言 在之前的文章中&#xff0c;我们主要介绍了什么是AXI协议&#xff0c;AXI协议的特点与优点&#xff0c;然后对于AXI协议非常重要的五通道结构进行了介绍&#xff0c;了解了5个通道各自的作用。本文我们继续AXI协议的学习&#xff0c;我们将讨论5个通道的具体内容和相对…

uni、js——点击与禁用(不可点击)、动态样式class

案例 没约满的时间可以点击进行选择&#xff0c;约满的就不能选择了。选择完之后变色变字。 核心思想就是创建一个第三方变量存起来&#xff0c;点击谁就存到第三方&#xff0c;在根据这个进行判断。 代码 <template><view class"content"><view cl…

JavaScript:基本语法(变量与函数的定义与使用)

文章目录 script 标签srcdefer 延迟加载 基本语法定义变量 与 使用变量基本类型typeof 查看变量类型复合类型数组类型定义对象类型定义 函数定义函数使用函数 script 标签 src 和scc一样可以内嵌也可以外src外引。 一般是推荐外引。 <script src"idx.js">&l…

opencv 进阶15-检测DoG特征并提取SIFT描述符cv2.SIFT_create()

前面我们已经了解了Harris函数来进行角点检测&#xff0c;因为角点的特性&#xff0c;这些角点在图像旋转的时候也可以被检测到。但是&#xff0c;如果我们放大或缩小图像时&#xff0c;就可能会丢失图像的某些部分&#xff0c;甚至有可能增加角点的质量。这种损失的现象需要一…