The C Programming Language(第 2 版) 笔记 / 5 指针与数组 / 5.4 地址算术运算

目录、参考文献


5.4 地址算术运算

如果 p 是一个指向数组中某个元素的指针,那么 p++ 将对 p 进行自增运算并指向下一个元素
p += i 将对 p 进行加 i 的增量运算,使其指向指针 p 当前所指向的元素之后的第 i 个元素
这类运算是指针或地址算术运算中最简单的形式

C 语言中的地址算术运算方法是一致且有规律的,将指针、数组和地址的算术运算集成在一起是该语言的一大优点
为了说明这一点,我们来看一个不完善的存储分配程序,它由两个函数组成
第一个函数 alloc(n) 返回一个指向 n 个连续字符存储单元的指针,alloc 函数的调用者可利用该指针存储字符序列
第二个函数 afree(p) 释放已分配的存储空间,以便以后重用
之所以说这两个函数是 “ 不完善的 ”,是因为对 afree 函数的调用次序必须与调用 alloc 函数的次序相反
换句话说,allocafree 以栈的方式(即后进先出的列表)进行存储空间的管理
标准库中提供了具有类似功能的函数 mallocfree,它们没有上述限制,我们将在 8.7 节中说明如何实现这些函数

最容易的实现方法是让 alloc 函数对一个大字符数组 allocbuf 中的空间进行分配
该数组是 allocafree 两个函数私有的数组
由于函数 allocafree 处理的对象是指针而不是数组下标,因此,其它函数无需知道该数组的名字
这样,可以在包含 allocafree 的源文件中将该数组声明为 static 类型,使得它对外不可见
实际实现时,该数组甚至可以没有名字,它可以通过调用 malloc 函数或向操作系统申请一个指向无名存储块的指针获得

allocbuf 中的空间使用状况也是我们需要了解的信息
我们使用指针 allocp 指向 allocbuf 中的下一个空闲单元
当调用 alloc 申请 n 个字符的空间时,alloc 检查 allocbuf 数组中有没有足够的剩余空间
如果有足够的空闲空间,则 alloc 返回 allocp 的当前值(即空闲块的开始位置),然后将 allocpn 以使它指向下一个空闲区域
如果空闲空间不够,则 alloc 返回 0
如果 pallocbuf 的边界之内,则 afree(p) 仅仅只是将 allocp 的值设置为 p

image

#define ALLOCSIZE 10000 /* size of available space */ 

static char allocbuf[ALLOCSIZE]; /* storage for alloc */ 
static char *allocp = allocbuf; /* next free position */
 
char *alloc(int n) /* return pointer to n characters */ 
{ 
    if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */ 
        allocp += n;
        return allocp - n; /* old p */ 
    } else /* not enough room */ 
        return 0; 
}

void afree(char *p) /* free storage pointed to by p */ 
{ 
if (p >= allocbuf && p < allocbuf + ALLOCSIZE) 
    allocp = p; 
}

一般情况下,同其它类型的变量一样,指针也可以初始化
通常,对指针有意义的初始化值只能是 0 或者是表示地址的表达式
对后者来说,表达式所代表的地址必须是在此前已定义的具有适当类型的数据的地址
例如,声明 static char *allocp = allocbuf;allocp 定义为字符类型指针,并将它初始化为 allocbuf 的起始地址
该起始地址是程序执行时的下一个空闲位置
上述语句也可以写成 static char *allocp = &allocbuf[0];
这是因为该数组名实际上就是数组第 0 个元素的地址

if 测试语句 if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */
检查是否有足够的空闲空间以满足 n 个字符的存储空间请求
如果空闲空间足够,则分配存储空间后 allocp 的新值最多比 allocbuf 的尾端地址大 1
如果存储空间的申请可以满足,alloc 将返回一个指向所需大小的字符块首地址的指针(注意函数的声明)
如果申请无法满足,alloc 必须返回某种形式的信号以说明没有足够的空闲空间可供分配
C 语言保证,0 永远不是有效的数据地址,因此,返回值 0 可用来表示发生了异常事件
在本例中,返回值 0 表示没有足够的空闲空间可供分配

指针与整数之间不能相互转换,但 0 是惟一的例外:常量 0 可以赋值给指针,指针也可以和常量 0 进行比较
程序中经常用符号常量 NULL 代替常量 0,这样便于更清晰地说明常量 0 是指针的一个特殊值
符号常量 NULL 定义在标准头文件 <stddef.h>

类似于 if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */
以及 if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
的条件测试语句表明指针算术运算有以下几个重要特点
首先,在某些情况下对指针可以进行比较运算
例如,如果指针 pq 指向同一个数组的成员,那么它们之间就可以进行类似于 ==!=<>= 的关系比较运算
如果 p 指向的数组元素的位置在 q 指向的数组元素位置之前,那么关系表达式 p < q 的值为真
任何指针与 0 进行相等或不等的比较运算都有意义
但是,指向不同数组的元素的指针之间的算术或比较运算没有定义
这里有一个特例:指针的算术运算中可使用数组最后一个元素的下一个元素的地址

指针可以和整数进行相加或相减运算
p + n 表示指针 p 当前指向的对象之后第 n 个对象的地址
无论指针 p 指向的对象是何种类型,上述结论都成立
在计算 p + n 时,n 将根据 p 指向的对象的长度按比例缩放,而 p 指向的对象的长度则取决于 p 的声明
例如,如果 int 类型占 4 个字节的存储空间,那么在 int 类型的计算中,对应的 n 将按 4 的倍数来计算

指针的减法运算也是有意义的
如果 pq 指向相同数组中的元素,且 p < q
那么 q - p + 1 就是从 pq 指向元素间的元素的数目(包括 pq
我们由此可以编写出函数 strlen 的另一个版本,如下所示:

/* strlen: return length of string s */ 
int strlen(char *s) 
{ 
    char *p = s; 
    while (*p != '\0') 
        p++; 
    return p - s; /* 这里没加 1 是因为字符串长度没有包括最后的 '\0' */
}

在上述程序段的声明中,指针 p 被初始化为指向 s,即指向该字符串的第一个字符
while 循环语句将依次检查字符串中的每个字符,直到遇到标识字符数组结尾的字符 '\0'
由于 p 是指向字符的指针,所以每执行一次 p++p 就将指向下一个字符的地址
p - s 则表示已经检查过的字符数,即字符串的长度
字符串中的字符数有可能超过 int 类型所能表示的最大范围
头文件 <stddef.h> 中定义的类型 ptrdiff_t 足以表示两个指针之间的带符号差值
但是,我们在这里使用 size_t 作为函数 strlen 的返回值类型,这样可以与标准库中的函数版本相匹配
size_t 是由运算符 sizeof 返回的无符号整型

指针的算术运算具有一致性:如果处理的数据类型是比字符型占据更多存储空间的浮点类型
并且 p 是一个指向浮点类型的指针,那么在执行 p++ 后,p 将指向下一个浮点数的地址
因此,只需要将 allocafree 函数中所有的 char 类型替换为 float 类型
就可以得到一个适用于浮点类型而非字符型的内存分配函数
所有的指针运算都会自动考虑它所指向的对象的长度

有效的指针运算包括:

  1. 相同类型指针之间的赋值运算
  2. 指针同整数之间的加法或减法运算
  3. 指向相同数组中元素的两个指针间的减法或比较运算
  4. 将指针赋值为 0 或指针与 0 之间的比较运算

其它所有形式的指针运算都是非法的,如:
两个指针间的加法、乘法、除法、移位或 mask 运算
指针同 floatdouble 类型之间的加法运算
不经强制类型转换而直接将指向一种类型对象的指针赋值给指向另一种类型对象的指针的运算(两个指针之一是 void * 类型的情况除外)


目录、参考文献

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值