根据优先级来分辨各类型
int p; //这是一个普通的整型变量
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3]; //过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
1.指针的类型
从语法的角度看,把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]
2.指针所指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。
3.指针的值(叫指针所指向的内存区或地址)
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)
4 指针本身所占据的内存区
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。
交换值不能用函数直接封装,要用指针来交换 例如:
把一个指定地址赋给p:
在定义指针时,“*”作为说明符。
使用指针时,“*”作为访问目标变量的运算符。
练习:输入三个数a,b,c,要求不管怎么输入,a,b,c都要按从小到大的顺序输出,用函数封装实现
可用一个指针变量指向一个数组元素:
int a[3] = {1,45,34};
int *p;
p=a;//把首元素的地址赋给指针变量p
使用指针来遍历数组:
arr++是不被编译器识别的,arr相当于是指针常量;arr++会给arr重新赋值,导致首元素地址改变
&arr是取整个数组的地址;
*arr----首元素;
int *arr与int arr[ ]这两种定义数组方式的疑问:
int * arr 实际定义的是一个指针,*arr 就是该指针指向的值等价于a[0],通过这个指针+1,+2可以得到挨着这个地址的下一个及下下个空间,所以*(arr+1)就等价于a[1]
int arr[ ]实际上根本没有定义指针(所谓的头指针是根本不存在的!)他实际上直接开辟一个空间,如果 int arr[5] , 那么直接开辟20个字节的空间,可以放五个整形数据 倒是arr是指向第一个元素,arr+1指向第二个元素,但是你如果去查一下 &arr 是查不到的,根本没有一个指针变量(所谓的头指针),而int * arr是货真价实的首先定义了一个指针,再通过它搞出一个数组!\n\n所以 同样要弄出一个长度为5的整形数组, 第一种方式会占用28个字节(64位机器)的空间,一个头指针,还有该指针指向的 20个字节的数组空间!
而第二种方式仅占用20个字节的数组空间,没有定义指针,所以不需要8字节来存放头指针!
数组指针
作用:使指针偏移的时候也能偏移对应大小字节的数组
int arr[2][2] = {{1,2},{3,4}};
int *p;
p=arr[0][0];//p++偏移4(一个整型变量)
p=arr;//p++偏移16(整个数组)
数组名与指针的关系
a;//代表数组首行地址,一般用a[0][0]的地址表示
&a;//代表整个数组的地址,一般用a[0][0]地址表示(在C语言中没有对整个数组地址的详细说明--->即首元素地址)
a[i];代表了第i行起始元素的地址
&a[i];代表了第i行的地址,一般用a[i][0]的地址表示
a[i]+j;//代表了第i行第j个元素地址,a[i]就是j==0的情况
a[i][j];//代表了第i行第j个元素
&a[i][j];//代表了第i行第j个元素的地址
*a;//代表数组a首元素地址也就是a[0]或者&a[0][0]
*(a+i);//代表了第i行首元素的地址,*a是i=0的情况
*(a+i)+j;//代表了第i行j个元素的地址
**a;//代表a的首元素的值也就是a[0][0]
*(*(a+i)+j);//代表了第i行第j个元素
题目:通过数组指针来寻找对应的行和列
函数指针
函数名就是地址
类似于数组指针
指针数组
定义:数组中的每一个元素都是指针变量
int *p[2];
int (*pfunc[4])(int,int);//定义一个函数指针数组
二级指针
int main(int argc,char *argv[]){…}
int main (int argc,char **argv){…}这两个指针是一样的
原因:C语言数组作为参数会退化为指针,作为形参,传的是数组的首地址,我们知道数组的首地址就是指针,传参传的是指针,首地址
一级指针取地址要用二级指针来承接:如下
segmentatiaon fault:段错误是指访问的内存超出了系统所给这个程序的内存空间。
做题中最常见的原因有两个:
第一是在某个函数内开的数组过大,导致该函数的栈无法容纳数组,造成爆栈。
解决方法:把数组开到函数外边,此时数组保存在全局变量区。
第二是有指针越界。
解决方法:检查是否有数组下标越界,或者定义的指针指向了错误的地方。