指针是一种用于保存变量地址的变量,其本质是变量,只不过这种变量用于保存变量的地址。指针的特殊之处有两点:
1、指针常常是表达某个计算的唯一途径;
2、同其他方法相比,使用指针通常可以生成更高效、更紧凑的代码。 这两点会在后面说到。此外,还必须注意到,指针和数组之间的关系不是一般的密切。
指针与地址
一元运算符”&”在这里作为取址运算符(此外,他还是按位与运算符),用于取一个对象的地址。
p = &c ;
将把变量c的地址赋给变量p,我们称p为“指向”c的指针。需要注意的是,取址运算符&只能应用于内存中的对象,即变量和数组元素,不能作用于表达式、寄存器和常量。
当*作用于指针变量时,将访问指针指向的对象。下面,我们假定x和y是整型变量,而ip是指向整形变量的指针:
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 0 */
ip = &z[0]; /* ip now points to z[0] */
其中,
int *ip;
该声明语句表明表达式*ip 的结果是 int 类型。这里,声明指针时需指明其类型,是因为指针只能指向特定类型的对象(一个例外情况是指向 void 类型的指针可以存放指向任何类型的指针)。
在 x 可以出现的任何上下文中都可以使用*ip,也就是说两者完全相等?那二者的差别在哪里呢?
一元运算符*和&的优先级比算术运算符的优先级高。
指针之间也可以相互赋值:
iq = ip ;
将把 ip 中的值拷贝到 iq 中, 这样,指针 iq 也将指向 ip 指向的对象。
指针与函数参数
由于 C 语言是以传值的方式将参数值传递给被调用函数。 因此, 被调用函数不能直接修改主调函数中变最的值。例如, 排序函数可能会用一个名为 swap 的函数来交换两个次序颠倒的元素。 但是, 如果将swap 函数定义为下列形式:
void swap(int x, int y) /* WRONG */
{
int temp;
temp = x;
x = y;
y = temp;
}
则下列语句无法达到该目 的。
swap( a, b);
这是因为,由于参数传递采用传值方式, 因此上述的 swap 函数不会影响到调用它的例程中的参数 a 和 b 的值。 该函数仅仅交换了 a 和 b 的副本的值。
调用函数swap( a, b)时,仅仅是将a赋值给x,将b赋值给y,不会对a和b产生任何影响。
那么, 如何实现我们的目标呢,可以使主调程序将指向所要交换的变量的指针传递给被调用函数, 即:
swap( &a, &b);
由于一元运算符&用来取变量的地址, 这样&a 就是一个指向变量 a 的指针。 swap 函数的所有参数都声明为指针, 并且通过这些指针来间接访问它们指向的操作数。
void swap(int *px, int *py) /* interchange *px and *py */
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
指针参数使得被调用函数能够访问和修改主调函数中对象的值。
我们来看这样一个例子:
函数 getint 接受自由格式的输入, 并执行转换, 将输入的字符流分解成整数, 且每次调用得到一个整数。 getint 需要返回转换后得到的整数, 并且, 在到达输入结尾时要返回文件结束标记。 这些值必须通过不同的方式返回。 EOF(文件结束标记) 可以用任何值表示, 当然也可用一个输入的整数表示。
下面的循环语句调用 getint 函数给一个整型数组赋值:
int n, array[SIZE] , getint( int *);
for (n = 0; n < SIZE && getint( &array[n] ) != EOF; n++)
每次调用 getint 时,输入流中的下一个整数将被赋值给数组元素 array[n] , 同时, n 的值将增加 1。 请注意, 这里必须将 array[n] 的地址传递给函数 getint, 否则函数 getint将无法把转换得到的整数传回给调用者。
该版本的 getint 函数在到达文件结尾时返回 EOF, 当下一个输入不是数字时返回 0, 当输入中包含一个有意义的数字时返回一个正值。
#include <ctype.h>
int getch(void) ;
void ungetch(int);
/* getint: get next integer from input into *pn */
int getint( int *pn)
{
int c, sign;
while (isspace(c = getch( ))) /* skip white space */
if (!isdigit( c) && c != EOF && c != ' +' && c ! = ' -') {
ungetch(c); /* it is not a number */
return 0;
}
sign = (c == '-' ) ? -1 : 1;
if (c == '+' || c == '-')
c = getch() ;
for (*pn = 0; isdigit(c) , c = getch())
*pn = 10 * *pn + (c - ' 0');
*pn *= sign;
if (c != EOF)
ungetch(c);
return c;
}
在 getint 函数中, *pn 始终作为一个普通的整型变量使用。其中还使用了 getch 和 ungetch两个函数, 借助这两个函数,函数 getint 必须读入的一个多余字符就可以重新写回到输入中。
指针与数组
通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快, 但另一方面, 用指针实现的程序理解起来稍微困难一些。
如果 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] 的内容。