从头学C(46)地址算术运算

0

C语言的地址算术运算方法是一致且有规律的,将指针、数组、地址的算术运算集成在一起,也是C语言的一大优点。

第五章 指针与数组 >> 5.4 地址算术运算

从上一节的学习中,我们已经知道了:如果 p 是一个指向数组中某个元素的指针,对 p 进行增量运算(如 p + i)可以使其指向 p 当前所指向的元素之后的第 i 个元素。这类运算可以说是指针或地址算术运算中最简单的形式了。

来看一个简单但并不完善的存储分配程序(之所以说是不完善的,因为对于连续的 alloc 函数调用,在释放时必须以相反的顺序依次调用 afree 函数):

#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
                return 0;
}

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

函数 alloc(n) 返回一个指向 n 个连续字符存储单元的指针,函数 afree(p) 释放已分配的存储空间,以便后续重用。

程序开始位置定义了一个外部变量 allocbuf(占10000个char类型存储空间的字符数组,作为“内存分配池”,当然这并不是严格意义上的内存池),作为 alloc 和 afree 的私有数组,该变量也是被声明为 static 类型,使其对外不可见。

使用指针 aloocp 来指向 allocbuf 中的下一个空闲单元,而且在程序的开始位置被初始化了,使其指向 allocbuf 的起始地址(即 &allocbuf[0])。通常,对指针有意义的初始化只能是 0 或者是表示地址的表达式(而且一定要是在此前已定义的具有适当类型的数据的地址)

在 alloc 函数中,通过 if 条件判断 allocbuf 中是否还有足够的空间分配,如果有,则返回分配的空间的首地址;如果没有足够的空间,则返回 0。C语言保证,0 永远不是有效的数据地址,因此,返回值 0 可以用来表示申请空间失败。

指针与整数之间不能相互转换,但 0 是唯一的例外,常量 0 可以赋给指针,指针也可以和 0 进行比较。很多地方都会用符号常量 NULL 代替常量 0,更清晰地说明常量 0 是指针的一个特殊值。

在 alloc 和 afree 函数中,都有 if 条件判断,对指针进行了比较和加减的算术运算,总结下来有这么几个特点:

  1. 如果指针 p 和 q 指向同一个数组的成员,他们之间可以进行类似于 ==、 != 、 < 、 >= 的关系比较运算。如果 p 指向的数组元素位置在 q 指向的数组元素位置之后,则 p > q。
  2. 如果指针 p 和 q 指向不同的数组的成员,它们之间的算术或比较运算时没有定义的。
  3. 任何指针与 0 进行 == 或 != 的比较运算是有意义的,用于表明指针是否有效。
  4. 指针可以和整数进行相加或相减运算。

关于指针和整数的相加/相减运算,需要特别说明是,结构 p + n 指向 p 当前所指对象之后的第 n 个对象的地址,无论指针 p 指向的对象是何种类型,该结论都成立。p 指向的对象的长度取决于 p 的声明,例如,int 类型占 4 个字节的存储空间,对应的 n 将按 4 的倍数来计算,如 p + 2 意味着 “地址 + 8”。

指针的减法和加法类似,所以表达式 allocbuf + ALLOCSIZE – allocp 表示 allocbuf 中空闲的存储空间。利用这个结论,我们可以写出另一个版本的 strlen 函数:

/* strlen: return length of string s */
int strlen(char *s) 
{
        char *p = s;

        while(*p != '\0')
                p++;
        return p - s;
}

指针 p 指向字符串 s 的第一个字符,通过 while 循环检查字符串中的每个字符,每执行一次 p++,p 就指向下一个字符,直到遇见字符串结束符’\0’,因此 p -s 表示已经检查过的字符数,即字符串长度。

指针的算术运算具有一致性:如果处理的数据类型比 char 型或 int 型占据更多的存储空间,如 float 类型,并且 p 是一个指向 float 类型的指针,那么在执行 p++ 后,p 将指向下一个浮点数的地址。所有的指针运算都会自动考虑它所指向的对象的长度

合法的指针运算包括:

  1. 相同类型指针之间的赋值运算;
  2. 指针同整数之间的加法/减法运算;
  3. 指向相同数组中元素的两个指针间的减法或比较运算;(注意加法是非法的)
  4. 将指针赋值为 0 ,或指针与 0 之间的比较运算。

其他所有形式的指针运算都是非法的,例如:

  1. 两个指针间的加法、乘法、除法、移位、屏蔽运算;
  2. 指针同 float 或 double 类型之间的加法运算;
  3. 不经强制类型转换,在不同类型指针之间进行直接赋值。

Leave A Reply