另一个需要注意的地方是这一行: int *p1, *p2; 声明了上例用到的两个指针,每个带一个星号(*),因为是这一行定义的所有指针都是整型int (而不是 int*)。原因是引用操作符(*) 的优先级顺序与类型声明的相同,因此,由于它们都是向右结合的操作,星号被优先计算。 指针和数组Pointers and arrays 数组的概念与指针的概念联系非常解密。其实数组的标识相当于它的第一个元素的地址,就像一个指针相当于它所指向的第一个元素的地址,因此其实它们是同一个东西。例如,假设我们有以下声明: int numbers [20]; int * p; 下面的赋值为合法的: p = numbers; 这里指针p 和numbers 是等价的,它们有相同的属性,唯一的不同是我们可以给指针p赋其它的数值,而numbers 总是指向被定义的20个整数组中的第一个。所以,p只是一个普通的指针变量,而与之不同,numbers 是一个指针常量(constant pointer),数组名的确是一个指针常量。因此虽然前面的赋值表达式是合法的,但下面的不是: numbers = p; 因为numbers 是一个数组(指针常量),常量标识不可以被赋其它数值。 由于变量的特性,以下例子中所有包含指针的表达式都是合法的:
a[5] = 0; // a [offset of 5] = 0 *(a+5) = 0; // pointed by (a+5) = 0 不管a 是一个指针还是一个数组名, 这两个表达式都是合法的。 指针初始化Pointer initialization 当声明一个指针的时候我们可能需要同时指定它们指向哪个变量, int number; int *tommy = &number; 这相当于: int number; int *tommy; tommy = &number; 当给一个指针赋值的时候,我们总是赋给它一个地址值,而不是它所指向数据的值。你必须考虑到在声明一个指针的时候,星号 (*) 只是用来指明它是指针,而从不表示引用操作符reference operator (*)。记住,它们是两种不同操作,虽然它们写成同样的符号。因此,我们要注意不要将以上的代码与下面的代码混淆: int number; int *tommy; *tommy = &number; 这些代码也没有什么实际意义。 在定义数组指针的时候,编译器允许我们在声明变量指针的同时对数组进行初始化,初始化的内容需要是常量,例如: char * terry = "hello"; 在这个例子中,内存中预留了存储"hello" 的空间,并且terry被赋予了向这个内存块的第一个字符(对应’h’)的指针。假设"hello"存储在地址1702,下图显示了上面的定义在内存中状态: 这里需要强调,terry 存储的是数值1702 ,而不是'h' 或 "hello",虽然1702 指向这些字符。 指针terry 指向一个字符串,可以被当作数组一样使用(数组只是一个常量指针)。例如,如果我们的心情变了,而想把terry指向的内容中的字符'o' 变为符号'!' ,我们可以用以下两种方式的任何一种来实现: terry[4] = '!'; *(terry+4) = '!'; 记住写 terry[4] 与*(terry+4)是一样的,虽然第一种表达方式更常用一些。以上两个表达式都会实现以下改变: 指针的数学运算Arithmetic of pointers 对指针进行数学运算与其他整型数据类型进行数学运算稍有不同。首先,对指针只有加法和减法运算,其它运算在指针世界里没有意义。但是指针的加法和减法的具体运算根据它所指向的数据的类型的大小的不同而有所不同。 我们知道不同的数据类型在内存中占用的存储空间是不一样的。例如,对于整型数据,字符char 占用1 的字节(1 byte),短整型short 占用2 个字节,长整型long 占用4个字节。 |