/*来源:《C语言教程——Al Kelley Ira Pohl著 徐波译》第四版
*有关c的一些东东,一段时间不用,忘的差不多了,一来记忆力不好,二来原先很少写c代码。
*这本教材很好,看了一段时间,做做笔记。
*/
指针
程序中的简单变量是在机器中某个特定的内存位置(又称地址)按照一定数量的字节存储的。程序中的指针就用于访问内存和操纵地址。
如果v是个变量,那么&v就是它所存储的值在内存中的位置,也就是地址。取地址操作符"&"是单目操作符,它和其他单目操作符的优先级相同,结合性也是从右向左。地址就是一组可以被操纵的值。程序可以声明指针变量,并使用他们来访问和操纵地址。下面这个声明
int *p;
把p声明为int类型的指针。它的合法取值范围总是包括特殊地址0以及一组在特定的C系统中被解释为地址的正整数。下面是对指针p进行赋值的一些例子:
p = 0;
p = NULL; /*equivalent to p=0; */
p = &i;
p = (int *) 1776; /*an absolute address in memory */
在第三个例子中,我们把p看成是“对i的引用”,“指向i”,或“包含了i的地址”。在第四个例子中,为了避免编译错误,这个强制类型转换是必要的。
间接访问或解引用操作符" * "是单目操作符,它的优先级和其他单目操作符相同。并且结合行也是从右向左。如果p是个指针,*p就是p的地址所包含的值。“间接访问”的说法取自机器变成语言。p的直接值是个内存地址,而*p是p的间接值,也就是p所存储的内存位置所存储的值。从某些意义上说,*是&的逆操作。我们想提供一个既简单有能清楚地说明指针工作机制的例子。假设有下面这个声明:
int a = 1,b = 2, *p;
此时,我们可以把变量a,b,p看成是这样存储在内存中的:

我们把p看成是个箭头,但由于它此时尚未赋值,因此我们不知道它指向哪里。假设下一行代码是:
p = &a;
我们把这行代码理解为“p被赋值为a的地址”,这样就有了下图:

现在,假设有这样一条赋值语句:
b = *p;
我们把它理解为“b被赋值为p所指向的值”,由于指针p指向a,因此下面两条语句是等价的:
b = *p;等价于 b = a;
让我们编写一个简单的程序,说明指针值与它所解引用的值之间的区别。我们将使用%p格式来打印指针的值,它在绝大多数系统中产生一个十六进制的值。在ANSI C系统中,%p格式是推荐使用的。
locate.c:
/* printing an address,or location. */
#include <stdio.h>
int main(void)
{
int i =7, *p = &i;
printf("%s%d\n%s%p\n"," Value of i: ", *p,"Location of i: ",p);
return 0;
}
在我们的系统中,这个程序的输出是:
Value of i: 7
Location of i: effffb24
指针可以在声明中进行初始化。变量p的类型是int * ,它的初始值是&i。另外,必须在取i的地址之前对它进行声明。变量在内存中的实际位置因系统而异。操作符*对p进行解引用。也就是说,p包含了一个地址(或位置),表达式*p就具有这个地址所存储的值,并根据p的声明类型进行正确的解释。
声明和初始化 | ||
int i=3,j=5,*p=&i,*q=&j,*r; double x; | ||
表达式 | 等价表达式 | 值 |
p==&i | p==( &i ) | 1 |
**&p | *(*(&p)) | 3 |
r=&x | r=(&x) | 非法值 |
7**p/ *q + 7 | (((7*(*p)))/(*q))+7 | 11 |
*(r=&j)*=*p | (*(r=&j)))*=(*p) | 15 |
在这张表中,我们试图把&x赋值给r。由于r是个int类型的指针,而表达式&x的类型是double,因此这样做是非法的。另外,注意在这张表中我们使用了表达式:
7**p/ *q+7
如果我们把它写成
7**p/*q+7 //少个空格
我们将会发现编译器将把/*当作注释的开始标记。这可能会导致一个难以发现的错误。
在传统C中,在不同类型的指针之间赋值是,进行转换通常是允许的。在ANSI C中,这种转换是不允许的,除非其中一种指针类型为void,或者赋值符右边是常量0.因此,我们可以把void *看成是一种通用的指针类型。这是一个非常重要的概念。
声明 | |
int *p; float *q; void *v; | |
合法赋值 | 非法赋值 |
p=0; | p=1; |
p=(int *) 1; | v=1; |
p=v=q; | p=q; |
p=(int *) q; |
当然,并不是每个值都存储在可访问的内存位置。记住下面这些禁忌是非常重要的:
无法由指针所指向的结构:
不要指向常量。
&3 /*illegal */
不要指向普通的表达式。
&(k+99) /*illegal */
不要指向寄存器变量。
register v;
&v /*illegal */
取址操作符可以作用于变量和数组元素。如果a是个数组,那么像&a[0]和&a[i+j+3]这样的表达式也是合法的。