指针与地址

一元运算符 & 可用于取一个对象的地址

1
p = &c;

表示把 c 的地址赋值给变量 p,我们称 p 是指向 c 的指针。地址运算符 & 只能应用于内存中
的对象,即变量与数组元素。它不能作用于表达式、常量或 register 类型的变量。

一元运算符 * 是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象。

1
2
3
4
5
6
int x = 1, y = 2, z[10];
int *ip; /* ip is a pointer to int */
ip=&x; /* ip now points to x */
y=*ip; /* y is now 1 */
*ip=0; /* x is now 1 */
ip = &z[0]; /* ip now points to z[0] */

指针只能指向某种特定类型的对象,也就是说,每个指针都必须指向某种特定的数据类型。

如果指针 ip 指向整型变量,那么在 x 可以出现的任何上下文中都可以使用 *ip,因此, 语句

1
*ip = *ip + 10;

由于指针也是变量,所以在程序中可以直接使用,而不必通过间接引用 的方法使用。例如,如果 iq 是另一个指向整型的指针,那么语句

1
iq = ip

将把 ip 中的值拷贝到 iq 中,这样,指针 iq 也将指向 ip 指向的对象。

指针与函数参数

C 语言是以传值的方式将参数值传递给被调用函数。因此,被调用函数不能直接修改主调函数中变最的值。

1
2
3
4
5
6
7
void swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}
swap(a, b);//无作用

如果要实现可以交换的目标的话可以如下:

1
2
3
4
5
6
7
void swap(int *px, int *py) {
int temp;
temp = *px;
*px = *py;
*py = temp;
}
swap(&a, &b);

指针与数组

例如有如下声明:

1
2
3
4
int a[10];
int *pa;
pa = &a[0]; // pa 是指向数组第一位的指针
x = *pa; // 把数组元素 a[0] 中的内容复制到变量 x 中

如果指针 pa 指向 a[0],那么 *(pa+1) 引用的是数组元素 a[1] 的内容, pa+i 是数组元素 a[i] 的地址,*(pa+i) 引用的是数组元素 a[i] 的内容。

无论数组 a 中元素的类型或数组长度是什么,上面的结论都成立。

但是,我们必须记住,数组名和指针之间有一个不同之处,指针是一个变量,因此,在 C 语言中,语句 pa=a 和 pa++ 都是合法的。但数组名不是变量,因此,类似于 a=pa 和 a++ 形 式的语句是非法的。

当把数组名传递给一个函数时,实际上传递的是该数组第一个元索的地址。

如下:

1
2
3
4
5
6
7
int strlen(char *s)
{
int n;
for (n = 0; *s != '\0', s++)
n++;
return n;
}

地址算术运算

如果 p 是一个指向数组中某个元素的指针,那么 p++ 将对 p 进行自增运算并指向下一个元素,而 p+=i 将对 p 进行加 i 的增量运算,使其指向指针 p 当前所指向的元素之后的第 i 个元素。

指针之间可以进行运算,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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;
}

如下 if 语句:

1
if (allocbuf + ALLOCSIZE - allocp >= n)

指针之间的相减比较的是两个指针之间的位置相差多少。

所以可以有另外的一个求字符串长度的版本:

1
2
3
4
5
6
7
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;
}

对指针有意义的初始化值只能是 0 或者是表示地址的表达式。

指针与整数之间不能相互转换,但 0 是惟一的例外:常量 0 可以赋值给指针,指针也可以和常量 0 进行比较。

有效的指针运算包括:

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

字符指针与函数

在字符串的内部表示中,字符数组以空字符 ‘\0’ 结尾,字符串常量占据的存储单元数也因此比双引号内的字符数大 1。

假定指针 pmessage 的声明如下:

1
char *pmessage;

那么,语句

1
pmessage ="now is the time";

将把一个指向该字符数组的指针赋值给 pmessage。

下面两个定义之间有很大的差别:

1
2
char amessage[] = "nw is the time"; /* 定义一个数组*/
char *pmessage = "now is the time"; /* 定义一个指针*/

amessage 是一个仅仅足以存放初始化字符串以及空字符’\0’的一维数组。数组中的单个字符可以进行修改,但 amessage 始终指向同一个存储位置。另一方面,pmessage 是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其它地址。

【参考资料】

  1. C程序设计语言

—EOF—