Chinaunix首页 | 论坛 | 博客
  • 博客访问: 560167
  • 博文数量: 166
  • 博客积分: 4038
  • 博客等级: 上校
  • 技术积分: 1115
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-14 23:29
文章分类

全部博文(166)

文章存档

2010年(12)

2009年(126)

2008年(28)

分类: C/C++

2009-03-11 12:53:31

上一讲已经说过,指针是一种变量,它也有自己的地址,但由于它是专门用来存放地址的变量,所以把它认为是种特殊的变量,既然有着特殊的身份,那么也理应受到特殊的待遇,下面来看看它享受了那些优待。

1.指针的定义

C语言中,定义一个普通的变量(如整型数),我们这样做:int i; 而定义一个指针变量(指针)我们需要这样做:int *p ;  还记得吗,一个矩形中的值是有类型的,可能是整型,可能是字符型……,它们原本是“清白”的,无类型的,是我们通过一些手段使它们有了类型。当我们做出int i; 这样一个定义时,编译器就会分配一个地址(例如200)并和i 关联起来,而int将限定编译器把这个区域中的内容作为整型数看待。

               矩形内的值被视为int   

现在我们又有了int *p; 这个定义,假设p是指向变量i的(见下图),p中存的是变量i的地址。* 表示p是一个指针,而int表示p中所存的地址对应的变量(即变量i)的类型是int

                             p指向i , int *p;中的int是指针p所指向的变量的类型

我们将int称为指针p的基类型,或指针p所指向的变量的类型。

类似地,我们可以有: char  *s ;    s是指向char型变量的指针

  float  *f ;     f是指向float型变量的指针

  double  *d ;   d是指向double型变量的指针

由此得到声明一个指针变量(指针)的一般形式 基类型  * 指针名;

有一点要注意,在定义指针时,以下两种方式都是允许的,例如:

int  *ptr1;

int*  ptr2;

但一般比较倾向用第一种,因为可以避免以下的误解:

int*  prt1,   ptr2;

这样的定义方式,容易使人误以为ptr2也是一个指针,事实上并不是,prt2是一个int型变量,以下的定义方式中ptr1ptr2才都是指针:

int*  ptr1,  *ptr2;

2.指针的运算

<1>&address-of operator)取地址操作符:

究竟如何使一个指针指向一个变量呢?后面的语句给出了解答:int *p = &i& 用于取一个对象的地址(本文说的对象是泛指的某一事物,如变量,数组等,和C++中的对象概念不同),这里用于将i的地址赋给p , 那么指针p就指向了变量i 。上述的语句也可以分开写,如:int *p;  p = &i;

小扩展:(下面大括号中的内容,出涉指针的朋友可以跳过,当然也可以作为扩展知识)

{&的实质:当对一个T类型对象进行 & 操作时,返回的是一个“指向T的指针”类型的常量,即指针常量(pointer constant),在我们使用&运算符时我们并不关心它是如何实现的,因为有编译器帮我们隐藏了这些细节。

可当我们想要对一个指针赋一个绝对地址的时候,这个问题就体现出来了,而且我们不得不去关注,在C语言中没有一种内建(built-in)的方法去表示指针常量,所以当我们使用它的时候通常先写成整型常量的形式,然后再通过强制类型转换把它转换成相应的类型,如:int * , double * , char *等。 所以后面所示的做法是不行的: int *p = 0x12345678 ; 正确的方式应为:int *p = (int *) 0x12345678; 也许大家还记得我在第一讲中说的要注意指针中只能存放地址,不能将一个非0值整型常量表达式或者其他非地址类型的数据赋给一个指针,原因就在此。在大多数计算机中,内存地址确实是以无符号整型数来表示的,而且多以16进制表示,但我们在C语言中不能用整型数去表示地址,只能用指针常量来表示,因为它是被用来赋给一个指针的。

对于这个赋值问题还可以换一个角度去理解,在C语言中,使用赋值操作符时,赋值操作符左边和右边的表达式类型应该是相同的,如果不是,赋值操作符将试图把右边表达式的值转换为左边的类型。所以如果写出int *p = 0x12345678 ; 这条语句编译器会报错:'=' : cannot convert from ' const int ' to ' int * ' ,因为赋值操作符左边和右边的表达式的类型应该相同,而0x12345678int型常量,p是一个指向int型的指针,两者类型不同,所以正确的方式是:int *p = (int *) 0x12345678 ; }

<2>* (Dereference operator) 解引用操作符

* 在定义时用来说明一个变量是指针,而在定义了一个指针之后,我们使用(引用)指针时,*p表示的是p所指向的对象(即i)。也就是说,对于一个已定义的指针使用 * 操作符,将访问这个指针所指向的对象,我们来看下面的程序:

#include

int main( )

{

int i;                    /* 定义一个int型变量i */

int *p;                  /* 定义一个指向int类型的指针p */

i = 2 ;                  /* 初始化i2 */

p = &i ;                /* i的地址赋给p ,即使p指向i */

printf("%d\n", i ) ;       /* 输出i的值 */

printf("%d\n", *p ) ;     /* 输出p所指向的存储单元的值,即i的值*/

return 0 ;             /* 标准C语言主函数应返回一个值,用以通知操作系统程序执行成功与否,通常0表示成功*/

}

程序输出结果为:

2

2

对于 * 操作符,由于它有两个等价的术语dereferenceindirection ,所以在国内的书籍中你会看到各种翻译方法,如:解引用、解除引用、反引用、反向引用、间接引用、间接访问……

只要你知道它是用来访问一个指针所指向的对象的,那么不管它叫什么都不重要了。还是那句话,弄懂是什么,不要在乎叫什么,如果你理解了它的真正含义,大可以简洁地称它为“星号”操作符!

3.指针的初始化

ANSI C定义了零指针常量的概念:一个具有0值的整形常量表达式,或者此类表达式被强制转换为void *类型,则称为空指针常量,它可以用来初始化或赋给任何类型的指针。也就是说,我们可以将00L'\0'2–20*5以及(void *)0赋给一个任何类型的指针,此后这个指针就成为一个空指针,由系统保证空指针不指向任何对象或函数。

ANSI C还定义了一个宏NULL,用来表示空指针常量。大多数C语言的实现中NULL是采用后面这种方式定义的:#define  NULL  ((void *)0)

对指针进行初始化时常用的有以下几种方式:

  1.采用NULL或空指针常量,如:int *p = NULL; char *p = 2-2; float *p = 0;

  2.取一个对象的地址然后赋给一个指针,如:int i = 3;  int *ip = &i

  3.将一个指针常量赋给一个指针,如:long *p = (long *)0xfffffff0;

  4.将一个T类型数组的名字赋给一个相同类型的指针,如:char ary[100]; char *cp = ary;

  5.将一个指针的地址赋给一个指针,如:int i = 3;  int *ip = &iint **pp = &ip;

  6.将一个字符串常量赋给一个字符指针,如:char *cp = “abcdefg”;

对指针进行初始化或赋值的实质是将地址或同类型(或相兼容的类型)的指针赋给它,而不管这个地址是怎么取得的。要注意的是:对于一个不确定要指向何种类型的指针,在定义它之后最好把它初始化为NULL,并在解引用这个指针时对它进行检验,防止解引用空指针。另外,为程序中任何新创建的变量提供一个合法的初始值是一个好习惯,它可以帮你避免一些不必要的麻烦。

4void *型指针

ANSI C定义了一种void *型指针,表示定义一个指针,但不指定它指向何种类型的数据。void *型指针作为一种通用的指针,可以和其它任何类型的指针(函数指针除外)相互转化而不需要类型强制转换,但不能对它进行解引用及下标操作。C语言中的malloc函数的返回值就是一个void *型指针,我们可以把它直接赋给一个其他类型的指针,但从安全的编程风格角度以及兼容性上讲,最好还是将返回的指针强制转换为所需的类型,另外,malloc在无法满足请求时会通过返回一个空指针来作为内存分配失败的信号,所以要注意返回值指针的判空。

5.指向指针的指针

在指针初始化的第5种方式中提到了用一个指针的地址来初始化一个指针。回忆一下上一讲的内容:指针是一种变量,它也有自己的地址,所以它本身也是可用指针指向的对象。我们可以将指针的地址存放在另一个指针中,如:

int i = 5000;

int *pi = &i;

int **ppi = π

此时的ppi即是一个指向指针的指针,下图表示了这些对象:

                        

i的地址为108pi的内容就是i的地址,而pi的地址为104ppi的内容即是pi的地址。对ppi解引用照常会得到ppi所指的对象,所获得的对象是指向int型变量的指针pi。想要真正地访问到i.,必须对ppi进行两次解引用,如下面代码所示:

printf("%d", i );

printf("%d", *pi );

printf("%d", **ppi );

以上三条语句的输出均为5000

阅读(783) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~