C语言KR圣经笔记 5.3指针和数组 5.4地址运算
5.3 指针和数组
在 C 语言中,指针和数组有着非常强的关联,强到应当把两者同时拿出来讨论。任何可以通过数组下标来做到的操作,也都能用指针来做到。而指针的版本通常会更快,但至少对初学者来说会更难理解。
如下声明
int a[10];
定义了一个大小为 10 的数组 a,即由10个名为 a[0], a[1], ... a[9] 的连续对象所组成的块。
用 a[i] 来表示数组的第 i 个元素。如果 pa 是指向整数的指针,其声明为
int *pa;
则赋值语句
pa = &a[0];
使 pa 指向 a 的第 0 个元素;也就是说, pa 包含了 a[0] 的地址。
现在如果再赋值
x = *pa;
会将 a[0] 的内容拷贝给 x。
如果 pa 指向数组的某个特定元素,则根据定义,pa + 1 会指向下一个元素,pa + i 指向 pa 之后的第 i 个元素, pa - i 指向 pa 之前的第 i 个元素。因此,如果 pa 指向 a[0],则
*(pa+1)
表示 a[1] 的内容,而 pa + i 表示 a[i] 的地址,而 *(pa+i) 是 a[i] 的内容。
其实,不管数组 a 里面的变量是什么类型,占据多大空间,上述说法都是正确的。“将指针加1” 的含义是 pa + 1 指向下一个对象,由此扩展到所有指针运算,可得 pa + i 指向 pa 之后的第 i 个对象。
下标和指针运算之间有非常紧密的关联。根据定义,类型为数组的变量或表达式,其值为数组第 0 个元素的地址。因此,经过如下赋值之后
pa = &a[0];
pa 和 a 有相同的值。由于数组的名称就是其首个元素位置的同义词,赋值 pa = &a[0] 也能够写成
pa = a;
而更令人惊奇(至少在首次看到时)的事实是,a[i] 也能够写成 *(a+i)。在计算 a[i] 时,C会立即将其转换为 *(a+i);两种形式是等价的。将操作符 & 分别应用到两者,就能得到 &a[i] 和 a+i 也是等价的:a+i 是 a 之后第 i 个元素的地址。从另一个角度看,如果 pa 是指针,可以对它加下标来使用;pa[i] 等价于 *(pa+i)。简而言之, 数组+下标的表达式,等价于指针+偏移的表达式。
数组名称和指针有一个区别必须牢记。指针是一个变量,因此 pa=a 和 pa++ 都是合法的。但数组名不是变量;像 a=pa 和 a++ 这样的结构是非法的。
当数组名称被传递给函数时,传递的是数组首元素的位置。在被调函数中,该参数是一个局部变量,因此数组名参数是一个指针,即一个包含地址的变量。我们可以利用这个事实来写另一个版本的 strlen,计算字符串的长度:
/* strlen: 返回字符串s的长度 */
int strlen(char *s)
{int n;for (n = 0; *s != '\0'; s++)n++;return n;
}
由于 s 是一个指针,对其递增是完全合法的;s++ 对调用 strlen 的函数里面的字符串不起任何效果,它仅仅是对该指针在 strlen 中的私有拷贝进行递增。这意味着如下调用:
strlen("hello, world"); /* 字符串常量 */
strlen(array); /* char array[100]; */
strlen(ptr); /* char *ptr; */
都是可行的。
作为函数定义中的形参
char s[];
和
char *s;
是等价的;我们更偏向后者,因为它更显式地表明该参数是指针。当数组名称被传递给函数时,函数可以根据自己的意愿来认定它处理的是数组还是指针,并进行对应的操作。如果能够让代码看起来更恰当、更清晰,甚至可以使用两种表示法。
通过传递指向子数组开头的指针,可以将数组的一部分传递给函数。例如,如果 a 是数组,则
f(&a[2])
和
f(a+2)
都是把从 a[2] 开头的子数组地址传给函数 f 。在函数 f 中,参数声明可以写为
f(int arr[]) { ... }
或是
f(int *arr) { ... }
从 f 的角度而言,参数指向的是大数组的一部分还是数组真实的首地址,都无关紧要。
如果能保证元素的存在,可以将数组向前索引;p[-1] 和 p[-2] 等在语法上都是合法的,它们都指向 紧挨着 p[0] 的之前的元素。当然,引用数组边界之外的元素是非法的。
5.4 地址运算
如果 p 是指向数组中某个元素的指针,则 p++ 将 p 递增以指向下一个元素,而 p+=i 将 p 递增 i 以指向当前元素后的第 i 个元素。这些及其类似的结构,是指针或地址运算的最简单形式。
C 语言地址运算的方式是一致而且有规律的;对指针,数组和地址运算的集成是 C 语言的优势之一。我们写一个简单原始的内存管理器来说明。有两个例程。第一个是 alloc(n),返回一个指针 p, 指向 n 个连续字符的位置,alloc 的调用者可以用它来保存字符。第二个是 afree(p),释放通过alloc 获取到的内存,使这块内存后续能被重用。说它们是“简单原始”的,因为必须以 alloc 相反的顺序来调用 afree 。也就是说,alloc 和 afree 管理的内存是一个栈,或者叫后进先出队列。标准库提供的类似函数叫做 malloc 和 free ,没有这个限制;在8.7节会展示如何来实现它们。
最简单的实现是让 alloc 交出一个我们称之为 allocbuf 的大字符数组中的一小部分。这个数组是 alloc 和 free 私有的。因为它们使用指针而不是下标来处理,其他例程不需要知道数组的名字,因此在包含 alloc 和 free 的源文件中,该数组可以声明为 static,使之对外部不可见。在实际的内存管理器中,数组甚至都不需要有名字;它可能是通过调用 malloc 或请求操作系统,从而获取到的一个指向未命名内存块的指针。
另一个所需的信息是 allocbuf 用了多少。我们使用一个指针 allocp 来指向下一个空闲的元素。当 alloc 被要求 n 个字符时,它检查在 allocbuf 中是否存在足够的空间。如果是,它返回当前的 allocp 值(即空闲块的开头),并将其递增 n,以指向下一个空闲区域。如果没有足够空间,alloc 返回零。afree(p) 仅仅是将 allocp 设为 p,如果 p 在 allocbuf 内部的话。
#define ALLOCSIZE 10000 /* 可用空间 */static char allocbuf[ALLOCSIZE]; /* alloc所用空间 */
static char *allocp = allocbuf; /* 下一个空闲位置 */char *alloc(int n) /* 返回指向n个字符的指针 */
{if (allocbuf + ALLOCSIZE - allocp >= n) { /* 空间足够 */allocp += n;return allocp - n; /* 旧的指针 */} else { /* 空间不足 */return 0;}
}void afree(char *p) /* 释放p指向的空间 */
{if (p >= allocbuf && p < allocbuf + ALLOCSIZE)allocp = p;
}
通常,指针可以像其他变量一样初始化,不过正常情况下,有意义的初始值只有零,或者是包含之前定义过且类型匹配的地址的表达式。如下声明
static char *allocp = allocbuf;
将 allocp 定义为字符串指针,并将其初始化为指向 allocbuf 的开头,即程序启动时的下一个空闲位置。这也可以写成
static char *allocp = &allocbuf[0];
因为数组名称正是其第0个元素的地址。
如下判断
if (allocbuf + ALLOCSIZE - allocp >= n) { /* 足够 */
用来检查是否有足够的空间可满足分配 n 个字符的请求。如果有,则 allocp 的新值最多能到达的位置比 allocbuf 的末尾元素还超过一个【注意这个地址已经不属于allocbuf了,只可用来比较,不能分配】。如果请求能够满足,alloc 返回指向一块字符的起始位置的指针(注意alloc函数的声明)。如果不能,alloc 必须能够返回指示空间不足的信号。C 语言保证 0 永远不会是数据的合法地址,因此返回值零用来指示不正常的事件,此时为空间不足。
指针和整数是不可以相互交换使用的。零是唯一的例外:常量零可以被赋给指针,且指针可以与常量零比较。通常用符号常量 NULL 作为助记符来代替零,以更清晰地表示这是指针的特殊值。NULL 在 <stdio.h> 中定义。此后我们都将使用 NULL。
如下判断
if (allocbuf + ALLOCSIZE - allocp >= n) { /* 足够 */
以及
if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
显示了指针运算的一些重要方面。首先,指针在某些环境下可以进行比较。如果 p 和 q 都指向同一数组的元素,则关系操作符如 ==,!=,<,>= 等等,都能正常使用。例如若要
p < q
为真,则 p 指向的元素在 q 指向的元素之前。任何指针都能与零进行相等或不等的比较,这是有意义的。但如果在不指向相同数组的指针之间进行运算或比较,其行为是未定义的。(有一个例外,数组末尾之后的第一个元素可以用于指针运算)
第二,我们已经观察到,指针和整数可以相加或相减。如下结构
p + n
表示 p 当前所指地址之后的第 n 个对象的地址。不管 p 指向何种类型的对象,这个说法总是正确的;n 会根据 p 指向的对象的大小进行放大,对象大小是由 p 的声明所决定的。例如,如果 int 占四个字节,则 int 会乘以四。【即如果p指向整数,则C编译器在计算 p + n 时会把n乘以4,例如 p=12345678 ,n=1,则 p+n = 12345678 + 1*4】
指针减法也是合法的:如果 p 和 q 指向同一数组内的元素,且 p<q,则 q-p+1 是 p 和 q 之间的元素个数(包含两端)。可用利用这个事实再写出另一个版本的 strlen:
/* strlen: 计算字符串s的长度 */
int strlen(char *s)
{char *p = s;while(*p != '\0')p++;return p - s;
}
在声明中,p 被初始化为 s,即字符串的首个字符。在 while 循环中,挨个检查每个字符,直到发现末尾的 '\0'。由于 p 指向的是字符,p++ 每次都将 p 移到下一个字符,而 p - s 表示移动过的字符数,即字符串的长度。(字符串中的字符数量可能太大,int 保存不下。头文件<stddef.h> 定义了一个类型 ptrdiff_t,它足够大,可以用来保存两个指针之间的有符号差值。然而,如果我们更仔细的话,会使用 size_t 来做 strlen 的返回值,以与标准库的版本相匹配。size_t 是 sizeof 操作符返回的有符号整型。)
指针运算是一致的:如果我们要处理比 char 占内存更多的 float,而 p 是指向 float 的指针,则 p++ 会指向下一个 float。这样,仅仅需要把 alloc 和 afree 中的所有 char 替换成 float,我们就能写出 alloc 的 float 版本 。所有的指针操作都会自动地考虑到所指向对象的大小。
合法的指针操作有:将指针赋给相同类型,一个指针与一个整数的加减,指向相同数组的两个指针的减法或比较,以及与零的赋值和比较。其他所有指针运算都是非法的。对两个指针相加是非法的,非法的还有相乘或相除,移位或者掩码,以及将指针与 float 或 double 相加,甚至,在没有强制类型转换的情况下,将一个类型的指针赋给另一个类型的指针。最后一种情况对 void * 是特例,它是可以不用强制类型转换。
相关文章:
C语言KR圣经笔记 5.3指针和数组 5.4地址运算
5.3 指针和数组 在 C 语言中,指针和数组有着非常强的关联,强到应当把两者同时拿出来讨论。任何可以通过数组下标来做到的操作,也都能用指针来做到。而指针的版本通常会更快,但至少对初学者来说会更难理解。 如下声明 int a[10]…...
设计模式:简单工厂模式、工厂方法模式、抽象工厂模式
简单工厂模式、工厂方法模式、抽象工厂模式 1. 为什么需要工厂模式?2. 简单工厂模式2.1. 定义2.2. 代码实现2.3. 优点2.4. 缺点2.5. 适用场景 3. 工厂方法模式3.1. 有了简单工厂模式为什么还需要有工厂方法模式?3.2. 定义3.3. 代码实现3.4. 主要优点3.5.…...
Could not load library libcudnn_cnn_infer.so.8
报错: Could not load library libcudnn_cnn_infer.so.8. Error: /root/miniconda3/lib/python3.10/site-packages/torch/lib/libcudnn_cnn_infer.so.8: undefined symbol: _ZNK10cask_cudnn14BaseKernelInfo18minorCCVCompatibleENS_8SafeEnumINS_47ComputeCapa…...
ELement UI时间控件el-date-picker误差8小时解决办法
一、问题描述: 在项目中引用了elementui中的date-picker组件,选中的时间跟实际相差八小时,且格式不是自己想要的格式 <el-date-pickertype"date"placeholder"选择日期"format"yyyy/M/d"v-model"form…...
Linux日志论转
系统日志、审计日志、诊断日志 日志系统rsyslog 日志管理基础: rsyslog 日志管理 logrotate日志轮转常见的日志文件 #tail -f /var/log/messages #动态查看日志文件的尾部,系统主日志文件#tail -f /var/log/secure #记录认证、安全的日志…...
第7课 利用FFmpeg将摄像头画面与麦克风数据合成后推送到rtmp服务器
上节课我们已经拿到了摄像头数据和麦克风数据,这节课我们来看一下如何将二者合并起来推送到rtmp服务器。推送音视频合成流到rtmp服务器地址的流程如下: 1.创建输出流 //初始化输出流上下文 avformat_alloc_output_context2(&outFormatCtx, NULL, &…...
Microsoft Visual Studio 2022 install Project 下载慢
1. 关闭Internet 协议版本6 2. 如果没有效果,打开Internet 协议版本4,更改DNS 3. 在浏览器中下载后安装,下载地址如下: Microsoft Visual Studio Installer Projects 2022 - Visual Studio Marketplace 4. 安装时注意关闭vs&…...
uniapp---安卓真机调试提示检测不到手机【解决办法】
最近在做APP,由于华为手机更新过系统,再次用来调试APP发现就不行了。下面给出具体的解决方法: 第一步:打开【允许开发人员选项】 找到【设置】点击【关于手机】找到【版本号】点击7次或多次,允许开发人员选项。 第二…...
Nginx(十四) 配置文件详解 - 负载均衡(超详细)
本篇文章主要讲ngx_http_upstream_module模块下各指令的使用方法。 1. upstream 上游服务器组/集群 Syntax: upstream name { ... } Default: — Context: http upstream指令定义了一个上游服务器组/集群,便于反向代理中的proxy_pass使用。服务器可以监听…...
大数据应用安全策略包括什么
大数据应用安全策略是为了保障大数据应用中的数据安全而采取的一系列措施,其重要性不容小觑。以下是大数据应用安全策略所包含的主要内容: 一、数据加密与安全存储 数据加密:对于敏感数据,应采用加密技术进行保护,包括…...
Ubuntu软件和vmware下载
https://cn.ubuntu.com/download/desktop VMware 中国 - 交付面向企业的数字化基础 | CN...
如何修改Anaconda的Jupyter notebook的默认启动路径
1.打开Anaconda控制台 2.输入下面的命令 jupyter notebook --generate-config 这个命令的作用是生成 Jupyter notebook 的配置文件。如果你是第一次运行,会直接生成这个文件。如果曾经运行过这个命令,就会像下图一样问你时候要覆盖原来的文件。这个时候…...
密码学:带密钥的消息摘要算法一数字签名算法
文章目录 前言手写签名和数字签名前置知识点:消息摘要算法数字签名算法数字签名算法的由来数字签名算法在实际运用的过程附加:签名和摘要值的解释 数字签名算法的家谱数字签名算法的消息传递模型经典数字签名算法-RSA实现 数字签名标准算法-DSA实现 圆曲…...
JVM中部分主要垃圾回收器的特点、使用的算法以及适用场景
JVM中部分主要垃圾回收器的特点、使用的算法以及适用场景: Serial GC(串行收集器) 特点:单线程执行,对新生代进行垃圾回收时采用复制算法(Copying),在老年代可能使用标记-压缩或标记…...
vue保姆级教程----深入了解Vuex的工作原理
📢 鸿蒙专栏:想学鸿蒙的,冲 📢 C语言专栏:想学C语言的,冲 📢 VUE专栏:想学VUE的,冲这里 📢 CSS专栏:想学CSS的,冲这里 Ǵ…...
(JAVA)-(网络编程)-InetAddress(ip)
InetAddress类就表示ip地址,他是一个接口,有两个实现类:Inet4Address和Inet6Address,分别表示IPv4和IPv6. 创建对象:此类没有对外提供构造方法,创建ip对象要使用InetAddress类的静态方法getByName()。 st…...
手把手带你死磕ORBSLAM3源代码(二十二)Tracking.cc PrintTimeStats介绍
目录 一.前言 二.代码 2.1完整代码 一.前言 这段代码是一个C++函数,名为Tracking::PrintTimeStats(),它属于Tracking类。这个函数的主要目的是计算和打印出各种跟踪和本地映射任务所花费的平均时间和标准差,并将这些信息同时输出到控制台和一个名为ExecMean.txt的文本文件…...
【操作系统xv6】学习记录3-Wsl2 Ubuntu18.04图形化界面
不知道为啥,wls2和windows用vscode ssh的方式连接识别, 既然如此,那就装一个桌面版的,其实2年多前装过一次,后来pc机跑深度学习的任务,硬盘坏了~ 开干前再试一次 ref:https://zhuanlan.zhihu.…...
CCNP课程实验-03-Route_Path_Control_CFG
目录 实验条件网络拓朴需求 基础配置需求实现1.A---F所有区用Loopback模拟,地址格式为:XX.XX.XX.XX/32,其中X为路由器编号。根据拓扑宣告进对应协议。A1和A2区为特例,A1:55.55.55.0/24,A2:55.55…...
STM32 学习(二)GPIO
目录 一、GPIO 简介 1.1 GPIO 基本结构 1.2 GPIO 位结构 1.3 GPIO 工作模式 二、GPIO 输出 三、GPIO 输入 1.1 传感器模块 1.2 开关 一、GPIO 简介 GPIO(General Purpose Input Output)即通用输入输出口。 1.1 GPIO 基本结构 如下图࿰…...
安卓作业001 - 显示学生信息
文章目录 安卓作业001 - 显示学生信息一、界面设计思路二、涉及知识点概览三、实现步骤详解四、启动应用,查看结果五、任务完成总结 安卓作业001 - 显示学生信息 显示学生详细信息要求更改应用图标及标 题要求设置窗口背景图片 一、界面设计思路 在本次安卓作业0…...
ARM CCA机密计算硬件架构之内存管理
实施了TrustZone安全扩展的Arm A-profile处理器呈现两个物理地址空间(PAS): 非安全物理地址空间安全物理地址空间Realm管理扩展增加了两个PAS: Realm物理地址空间Root物理地址空间下图显示了这些物理地址空间以及如何在工作系统中实施这些空间: 正如表格所示,根状态能够访…...
MySQL--安装与配置与向日葵的基本操作使用
一.MySQL介绍 1.1 MySQL简介 MySQL是一个开源的关系型数据库管理系统,最早由瑞典MySQL AB公司开发。这个数据库系统有着高可靠性、高性能和易用性的特点,在互联网上得到了广泛的应用。MySQL支持SQL语言,可以运行在多种操作系统上,…...
Vue - 多行文本“展开、收起”功能
TextClamp 使用 js 实现文本展开、收起,并非纯 CSS 实现。 Props: fontSize:Number,默认:14lines:Number,默认:1lineHeight:Number,默认:20 F…...
Linux操作系统基础(6):Linux的文件颜色
1. Linux文件颜色 在Linux系统中,文件和目录的颜色是通过终端的配置来实现的,不同的颜色代表不同类型的文件或目录。通常情况下,可以通过 LS_COLORS 环境变量来配置文件和目录的颜色。下面是一些常见的颜色及其代表的含义: 白色…...
LeetCode 1758. 生成交替二进制字符串的最少操作数【字符串,模拟】1353
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
Spring-IOC-xml方式
简介 **控制反转**(Inversion of Control,缩写为**IoC**),是[面向对象编程]中的一种设计原则,可以用来减低计算机[代码]之间的[耦合度]。其中最常见的方式叫做[依赖注入]Dependency Injection,简称DI&#…...
HUAWEI华为荣耀MagicBook X 15酷睿i5-10210U处理器集显(BBR-WAH9)笔记本电脑原装出厂Windows10系统
链接:https://pan.baidu.com/s/1YVcnOP5YKfFOoLt0z706rg?pwdfwp0 提取码:fwp0 MagicBook荣耀原厂Win10系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、华为/荣耀电脑管家等预装程序 文件格式:esd/wim/swm 安装…...
React使用动态标签名称
最近在一项目里(React antd)遇到一个需求,某项基础信息里有个图标配置(图标用的是antd的Icon组件),该项基础信息的图标信息修改后,存于后台数据库,后台数据库里存的是antd Icon组件…...
Java异常篇----第二篇
系列文章目录 文章目录 系列文章目录前言一、 Excption与Error包结构二、Thow与thorws区别三、Error与Exception区别?四、error和exception有什么区别前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女…...
wordpress 查看用户密码/游戏推广员平台
大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm。大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间星期四 14:04,因为前面两字符串中第 1 对相同的大写英…...
伊春网站推广/一键优化
根据参考一些网络资料收集整理:因为使用代码添加的 每一次添加一个控件容器控件内部都做了自动计算 主要在这两句代码headerRow.Cells.Add(headerCell);detailRow.Cells.Add(detailCell);向XRTableRow 添加 XRTableCell 第一次新添加的会XRTableCell会以XRTableRow 的宽度值第二…...
湖南建设监理官方网站/ip域名查询网站入口
1.创建用户 在命令行中执行以下操作: Step1 创建用户 sudo useradd username -m 注意要在后面加-m,否则不会在home路径下创建该用户的文件夹 创建好之后可以在/home/路径下查看该用户名的文件夹 在CLI中执行cat /etc/passwd可以查看passwd文件中是否有…...
主题资源网站建设反思/互联网seo是什么
加入 PolkaWorld 社区,共建 Web 3.0!波卡提供了代理模块,你可以通过它来设置代理账户,从而增加一层安全性。本文是波卡百科(Polkadot Wiki)的一部分。Polkadot Wiki 是由波卡官方撰写的波卡介绍文档,PolkaWorld 正在逐…...
dz论坛做视频网站教程/目前搜索引擎排名
亲爱的忍忍:为了给忍忍们带来更好的游戏体验,《忍者必须死3》预计于2021年3月25日2:00-10:00进行停服维护(根据具体情况提前或推迟开放)。请各位忍忍相互转告,并合理安排游戏时间,以免造成损失。【维护时间】2021年3月25日2:00-10:00【更新内…...
流量型网站 cms/制作网站费用
链接地址:http://www.wrclub.net/news/listnews.aspx?id2630ANDhttp://www.csdn.net/news/newstopic/17/17128.shtml...