Linux系统中指针的详细分析与操作
文章目录
一、指针
二、指针的初始化
三、指针的运算
四、指针与数组
五、指针与字符串
六、函数指针
七、NULL 指针
八、对复杂指针的解释
C
语言指针真正精髓的地方在于指针可以进行加减法,这一点极大的提升了程序的对指针使用的灵活性,同时也带来了不小的学习负担。正是因为 C
语言指针可运算,才奠定了如今 C
语言的地位。
一、指针
对于内存,我们可以简单地认为它就是大小相同、连续排布的格子,每一个格子的大小为一字节。为了更方便地找到某一个格子,我们通过对内存进行编号,通过编号来找到某一个具体的内存格子。
这样的编号通常称之为内存地址,如果程序想要获取某一块内存存放的数据,必须通过内存地址定位,再取出对应内存的数据。
一个指针变量存储着另一块内存的起始地址,相较于直接寻址的方式,如果想要通过一个指针获取指向的内存变量的话,首先需要获取到指针变量存储的内存地址,再通过这个地址来获取变量,所以这种方式又称为间接寻址。
在 C
函数实现中,所传入的参数均为原有变量的一个拷贝,在函数中对参数进行修改是无法影响到原有变量的值的,若需要对参数进行修改,可向函数传递该变量的内存地址。如此一来,即使对参数(地址)进行了拷贝,也可以通过地址对其进行修改,而变量的地址就保存在指针当中。
二、指针的初始化
操作符 &
表示取一个变量的地址,并将该地址赋给变量 pointer
,由此一来指针中的地址就是正确且合法的。
void main() {int number = 100;int *pointer;pointer = &number;
}
三、指针的运算
C
语言中的指针支持运算,不过仅支持加减法,并不支持乘除法。在前面我们已经提到过了,一元运算符 *
表示间接寻址,即取出指针的地址,根据该地址找到指针指向的变量。而对于指针的加减法而言,与指针指向的类型密切相关。
可以看到,对一个指向 int
的指针加 1
,其地址增加了 4
,正好是一个整型的大小。其原因在于如果仅仅是增加 1
的话,那么指针将会指向该整型的第二个字节,那么此时指针所指向的“整型”值将会变得非常奇怪,因为最后一个字节是其它数据的。
四、指针与数组
我们经常听到数组名称其实就是一个指针,数组名表示数组的首地址。但是这并不正确,数组名称和指针绝不等价,我们可以认为数组名称具有指针的特性,但不能说数组和指针等价。一个最为典型的例子就是使用 sizeof
求数组的长度。
#include <stdio.h>void main() {int number[] = {1, 2, 3};int *pointer = number;printf("number's size: %ld \n", sizeof(number));printf("pointer's size: %ld \n", sizeof(pointer));
}// number's size: 12
// pointer's size: 4
在上述代码中我们明确地表示了 number
是一个整型数组,表示整型的集合,所以 sizeof
的结果为数组占用内存的总大小。而 pointer
仅仅只是一个整型指针,编译器并不知道它到底是指向一个整型,还是一坨整型,那么 sizeof
的结果自然而然的是指针占用内存空间的大小。
所以,数组名称所代表的含义要高于指针,但是这并不改变数组名是一个指针的事实。数组在内存中连续分布,再结合前面所提到的指针的运算,使得我们可以通过指针的方式访问数组中的元素。
五、指针与字符串
C
语言中字符串并非像其它语言一样,将其设置为基本数据类型,而是构建于数组之上,并在数组末尾添加 \0
表示结尾,这也是为什么数组和字符串经常成对出现的原因。
int main() {char *hello = {"Hello"};char world[] = {"World"};printf("hello: %s \n", hello);printf("world: %s \n", world);
}
如上述代码所示,我们既可以使用数组初始化一个字符串,也可以使用字符指针来初始化一个字符串,只不过使用字符指针初始化的字符串具有只读特性,使用数组初始化的字符串具有读写特性。
六、函数指针
在函数式编程中我们经常需要将一个函数作为参数传递给另外一个函数,其中比较经典的例子线程的创建与执行。不同于 fork
或者 vfork
调用,pthread_create
调用以一个函数指针作为参数,创建的线程转而执行该函数。
int filterArray(int *array, int size);
上述代码是一个简单的函数原型,接收一个整型数组和其长度,并返回一个整型。一个函数总是占用一段连续的内存区域,函数名称在某些情况下会被转换成该函数所在内存区域的首地址,这一点和数组非常的相似。也就是说,函数名称 filterArray
可以被替换成 (*pointerName)
,这样一来就得到了一个指向函数入口的指针:
int (*pointerName)(int *array, int size);
所以,函数指针其实就是对函数原型的稍加修改而已。
七、NULL 指针
标准定义了 NULL
指针,它作为一个特殊的指针变量,表示不指向任何东西,当我们在定义一个指针变量,并且不知道该指针指向何处时,就应该将其初始化为 NULL
。其次,当我们使用 free
函数释放堆内存时,也应该将指针指向为NULL
,防止出现内存访问错误。
#include <stdlib.h>int main() {char *s = NULL;s = (char *)malloc(sizeof(char) * 5);strcpy(s, "hello");free(s);s = NULL;return 0;
}
对NULL
指针进行解引用是非法的,因为 NULL
指针从定义上来看不指向任何东西。因此,当我们对指针进行解引用时应该对其进行检查,能显著减少 DEBUG
的时间。
八、对复杂指针的解释
有时候我们会看到诸如 char *(*name[124])(int **p)
这种看起来非常复杂的指针,对于此类复杂指针,我们只需要牢记两个关键点即可。
-
对于一个符号定义而言,找到其名称,然后再按照优先级顺序进行解析。
-
牢记
C
语言中操作符的优先级。
操作符 | 描述 |
---|---|
() | 聚组,例如5 / (2 + 3) ,和数学中的()语义相同 |
() | 函数调用,例如 3 + foo() ,首先调用函数,而后执行加法操作 |
[] | 下标引用,例如 foo()[2] ,首先调用函数,根据函数返回的结果进行下标引用 |
. | 访问结构体成员,如 student[1].name 首先获取数组索引为1 的元素,而后访问name 成员 |
-> | 访问结构指针成员,和 . 作用相同 |
++ | 后缀自增,如 struct.name++ ,首先获取结构体成员再对其进行自增 |
– | 后缀自减,同后缀自增 |
! | 逻辑反 |
~ | 按位取反 |
++ | 前缀自增 |
– | 前缀自减 |
* | 间接访问,如 *p++ 首先对指针 p 进行自增,然后对自增后的指针进行间接访问 |
& | 取变量地址 |
可以看到,*
间接访问操作的优先级要远远低于()
、[]
、++
以及 .
等常用操作符,也就是说,*
在复杂指针中就是个弟弟。
接下来就对 char *(*name[124])(int **p)
这个指针做一个具体的分析。首先得找到变量名称,上面有两个: name
以及 p
,结合外面两对括号可知这是个函数指针,p
为函数参数。再来看 *name[124]
,[]
的优先级高于 *
,所以 name
是一个数组,数组中保存了某种类型的指针。将所有的信息整合起来,就可以得到:name
是一个指针数组,保存了原型为 char *funcName(int **p)
的函数指针。
相关文章:

Linux系统中指针的详细分析与操作
文章目录 一、指针 二、指针的初始化 三、指针的运算 四、指针与数组 五、指针与字符串 六、函数指针 七、NULL 指针 八、对复杂指针的解释 C 语言指针真正精髓的地方在于指针可以进行加减法,这一点极大的提升了程序的对指针使用的灵活性,同时也…...

工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)
博主的合并代码gitgithub.com:huashu996/VINS-FUSION-ESDFmap.git一、D435i深度相机配置1.1 SDKROS参考我之前的博客,步骤和所遇见的问题已经写的很详细了https://blog.csdn.net/HUASHUDEYANJING/article/details/129323834?spm1001.2014.3001.55011.2 相机标定参数…...

第十四届蓝桥杯三月真题刷题训练——第 4 天
目录 题目 1 :九数算式_dfs回溯(全排列) 题目描述 运行限制 代码: 题目2:完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码: 题目 1 &am…...

Hadoop 运行环境搭建(开发重点)
文章目录Hadoop 运行环境搭建(开发重点)一、安装JDK二、安装配置 Hadoop1、安装 hadoop2、hadoop 目录结构3、设置免密登录4、完全分布式模式(开发重点)1)分发jdk2)集群配置(1) 集群部署规划(2) 配置文件说…...

在社交媒体上行之有效的个人IP趋势
如果您认为无论是获得一份工作、建立一家企业还是推动个人职业发展,社交媒体都是帮助您实现目标的可靠工具,那么个人IP就是推动这一工具前进的燃料。个人IP反映了您是谁,您在所处领域的专业程度,以及您与他人的区别。社交媒体将有…...
Java网络编程
网络编程 什么是网络编程? 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信) Java.net. 包下提供了网络编程的解决方案* 基本的通信架构 基本的通信架构有两种方式:CS架构(Client客户端/Se…...
PTA:L1-001 Hello World、L1-002 打印沙漏、L1-003 个位数统计(C++)
目录 L1-001 Hello World 问题描述: 实现代码: L1-002 打印沙漏 问题描述: 实现代码: 原理思路: L1-003 个位数统计 题目描述: 实现代码: 原理思路: 过于简单的就不再写题…...

构造HTTP请求
使用formform使用如下:<body><!-- 表单标签,允许用户和服务器之间交互数据 --><form action"https://www.sogou.com" method"get"><!-- 要求提交的数据以键值对的结构来组织 --><input type"text" name"stduent…...
转速/线速度/角速度计算FC
工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…...

学习笔记:Java并发编程(补)ThreadLocal
【尚硅谷】学习视频:https://www.bilibili.com/video/BV1ar4y1x727【黑马程序员】学习视频:https://www.bilibili.com/video/BV15b4y117RJ 参考书籍 《实战 JAVA 高并发程序设计》 葛一鸣 著《深入理解 JAVA 虚拟机 | JVM 高级特性与最佳实践》 周志明 著…...

HashMap底层实现原理及面试题
文章目录1. 常见的数据结构有三种结构1.1 各自数据结构的特点2. HashMap2.1 概述2.2 底层结构2.2.1 HashMa实现原理:2.2.1.1 map.put(k,v)实现原理2.2.1.2 map.get(k)实现原理2.2.1.3 resize源码2.2.2 HashMap常用的变量2.2.3 HashMap构造函数2.3 JDK1.8之前存在的问…...
【STM32】进阶(二):DMA+ADC实现模拟量检测
1、简述 DMA:Direct Memory Access,直接内存访问 ADC:Analog to Digital Converter,模数转换器,模拟信号转换成数字信号的电路(采样-量化-编码) 参考博客: STM32DMA功能详解 STM32…...

Lab2_Simple Shell_2020
Lab2: 实验目的:给xv6添加新的系统调用 并理解系统调用是如何工作的,并理解xv6内核的一些内部特征 实验准备: 阅读xv6的第2章以及第4章的4.3,4.3小节熟悉下面的源码 用户态相关的代码:user/user.h和user/usys.pl内核态相关的代…...

2023最全电商API接口 高并发请求 实时数据 支持定制 电商数据 买家卖家数据
电商日常运营很容易理解,就是店铺商品维护,上下架,评价维护,库存数量,协助美工完成制作详情页。店铺DSR,好评率,提升客服服务等等,这些基础而且每天都必须做循环做的工作。借助电商A…...
MySQL 的索引类型
1. 按照功能划分 按照功能来划分,索引主要有四种: 普通索引唯一性索引主键索引全文索引 普通索引就是最最基础的索引,这种索引没有任何的约束作用,它存在的主要意义就是提高查询效率。 普通索引创建方式如下: CREATE…...

< Linux > 进程信号
目录 1、信号入门 生活角度的信号 技术应用角度的信号 前台进程 && 后台进程 信号概念 用kill -l命令察看系统定义的信号列表 信号处理的方式 2、信号产生前 用户层产生信号的方式 3、产生信号 3.1、通过终端按键产生信号 3.2、核心转储core dump 3.3、调用系统函数…...

Pyspark基础入门7_RDD的内核调度
Pyspark 注:大家觉得博客好的话,别忘了点赞收藏呀,本人每周都会更新关于人工智能和大数据相关的内容,内容多为原创,Python Java Scala SQL 代码,CV NLP 推荐系统等,Spark Flink Kafka Hbase Hi…...

C/C++每日一练(20230307)
目录 1. 国名排序 ★★ 2. 重复的DNA序列 ★★★ 3. 买卖股票的最佳时机 III ★★★ 🌟 每日一练刷题专栏 C/C 每日一练 专栏 Python 每日一练 专栏 1. 国名排序 小李在准备明天的广交会,明天有来自世界各国的客房跟他们谈生意,…...

一条SQL查询语句是如何执行的?
平时我们使用数据库,看到的通常都是一个整体。比如,你有个最简单的表,表里只有一个ID字段,在执行下面这个查询语句时: mysql> select * from T where ID10; 我们看到的只是输入一条语句,返…...

tcsh常用配置
查看当前的shell类型 在 Linux 的世界中,有着许多 shell 程序。常见的有: Bourne shell (sh) C shell (csh) TC shell (tcsh) Korn shell (ksh) Bourne Again shell (bash) 其中,最常用的就是bash和tcsh,本次文章介绍tcsh的…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)
引言 在嵌入式系统中,用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例,介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单,执行相应操作,并提供平滑的滚动动画效果。 本文设计了一个…...

SQL注入篇-sqlmap的配置和使用
在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap,但是由于很多朋友看不了解命令行格式,所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习,链接:https://wwhc.lanzoue.com/ifJY32ybh6vc…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...