1: int p; //定义整型变量
2: int *p; //定义一个整型指针
3: int p[3]; //定义一个整型数组
4: int *p[3]; //定义一个整型指针数组([]操作符优先级高于*操作符)
5: int (*p)[3]; //定义一个指向数组的指针
6: int **p; //定义一个指针指针
7: int p(int); //定义一个有一个整型参数且返回值为整型的函数
8: int (*p)(int); //定义一个指向有一个整型参数且返回值为整型的函数的指针
9: int *(*p(int))[3]; //定义一个参数为整型返回值指向由整型指针组成的数组的函数
指针是一个特殊的变量(常量),它里面存储的数值被解释成为内存里的一个地址。
指针的类型:
从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
1: int *ptr; //指针类型:int *
2: char *ptr; //指针类型:char *
3: int **ptr; //指针类型:int **
4: int (*ptr)[3]; //指针类型:int (*)[3]
5: int *(*ptr)[4]; //指针类型:int *(*)[4]
指针所指向的类型:
从语法上看,把指针声明语句中的指针名字和名字左边的指针说明符*去掉,剩下的就是指针所指向的类型。
1: int *ptr; //指向:int
2: char *ptr; //指向:char
3: int **ptr; //指向:int *
4: int (*ptr)[3]; //指向:int ()[3]
5: int *(*ptr)[4]; //指向:int *()[4]
指针指向的地址:
指针的值是指针本身存储的数值,这个值被编译器当做一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里的内存地址全都是32位长。指针指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof的一片内存区。指针指向了某块内存区域的首地址。
指针本身所占据的内存区:
用sizeof操作符可以获得指针本身占了多大内存。在32位平台里,指针本身占据了4个字节的长度。
指针的算术运算:
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值加减运算的意义不一样,以单元为单位。一个指针加或减一个整数n后,结果是一个新的指针。新指针和原来的指针类型相同,所指向的类型也相同。新指针比原来的指针的值增加或减少了n*sizeof(指针所指向类型)个字节。
两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向不明,而且毫无意义。两个指针刻意进行减法运算,但类型必须相同。
运算符&和*:
&是取地址运算符,*是间接运算符。
&a的运算结果是a的地址,*p的运算结果是p所指向的东西,它的类型是p指向的类型,所占用的地址是p所指向的地址。
指针表达式:
一个表达式的结果如果是一个指针,那么这个表达式就叫指针表达式。
由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型、指针所指向的类型、指针指向的内存区和指针自身占据的内存。
当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。
数组和指针的关系:
数组的数组名可以看做一个指针。指向数组的第0个元素。
字符串相当于一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值,如果看成指针的话,既是常量指针也是指针常量。
在表达式sizeof(array)中,数组名array代表数组本身,这时sizeof操作符测出的是整个数组的大小。
在表达式*array中,array是指针,表达式的结果就是数组第0个元素的值。sizeof(*array)测出的是数组元素的大小。
数组长度=sizeof(array)/sizeof(*array)
sizeof测出的都是对象自身类型的大小。
指针和结构类型的关系:
可以声明一个指向结构类型对象的指针。
1: struct mystruct
2: {
3: int a;
4: int b;
5: int c;
6: };
7:
8: struct mystruct ss={20,30,40};
9: struct mystruct *ptr=&ss;
可以通过指向运算符->来使用指针访问结构成员变量。
所有C/C++编译器在排列数组的元素时,总是把各个数组元素存放在连续的存储区里,元素和元素之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐、双字对齐或其他对齐方式,需要在相邻两个成员之间加若干个“填充字节”。这将导致各个成员之间可能会有若干个字节的空隙。所以不能通过*(ptr+n)的方式来访问结构成员。
指针和函数的关系:
可以把一个指针声明成为一个指向函数的指针。
1: int fun1(char *,int);
2: int (*pfun1)(char *,int);
3: pfun1=fun1;
4: int a=(*pfun1)(“xxxxxxxx”,8);
可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
指针类型转换:
如果有一个指针p,需要把它的类型和所指向的类型改为TYPE *TYPE,那么语法格式是:(TYPE *)p;
强制转换的结果是一个新指针,该指针的类型是TYPE *,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。
一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,必须保证类型一致,否则需要强制转换。
指针的安全问题:
在使用指针时,必须非常清楚指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。
在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。