C语言中的精华是什么,那当然是指针,是C语言的难点部分。C是对底层操作非常方便的语言,而底层操作中用到最多的就是指针,
这成就了优秀的C程序的效率几乎和汇编语言程序一样高的功绩。
本文介绍C指针的一些基础和高级知识。关键好是多写代码,这样才能更好的理解C的精华--指针。
1. 指针的概念
指针是一种数据类型,与其它的数据类型不同的是指针是一种“用来存放地址值的”变量。
首先搞懂这几个概念:
指针类型、指针指向数据类型、指针指向的内存区、指针在内存中占空间大小.
如:int *pn;
指针类型: 去掉变量名,所剩就是指针类型,可知 pn 的指针类型为 int * ;
指针所指数据类型: 去掉变量名及 *, int 即为指针指向数据的类型;
指针指向的内存区:指针指向数据类型为多大,那么指针指向的内存区大小就为多少。
指针在内存中所占空间大小:在 32位的系统上, 指针在内存中始终是一个占了4个字节的数据类型。
再介绍下C里面特殊类型的指针,void指针:
void的意思就是“无值”或“无类型”。void指针一般称为“通用指针”或“泛指针”, 使用void指针可以很容易地把void指针转换成其它数据类型的指针.
分析指针,可以从变量名处起,根据运算符优先级结合,一步一步分析.
下面举例分析:
int p;//这是一个普通的整型变量
int *p;//首先从P处开始,先与*结合,所以说明P是一个指针,然后再与int结合,说明指针所指向的内容的类型为int型.所以P是一个返回整型数据的指针
int p[3];//首先从P处开始,先与[]结合,说明P是一个数组,然后与int结合,说明数组里的元素是整型的,所以P是一个由整型数据组成的数组
int *p[3];//首先从P处开始,先与[]结合,因为其优先级比*高,所以P是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int结合,说明指针
所指向的内容的类型是整型的,所以是一个由返回整型数据的指针所组成的数组
int (*p)[3];//首先从P处开始,先与*结合,说明P是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,
然后再与int结合,说明数组里的元素是整型的.所以P是一个指向由整型数据组成的数组的指针
int **p;//首先从P开始,先与*结合,说明P是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int结合,说明该指针所指向的元素是整型数据.
所以P是一个返回指向整型数据的指针的指针
int p(int);//从P处起,先与()结合,说明P是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数然后再与外面的int结合,说明函数的返回值是一个
整型数据.所以P是一个有整型参数且返回类型为整型的函数
int (*p)(int);//从P处开始,先与指针结合,说明P是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int结合,说明函数有一个int型的参数,
再与最外层的int结合,说明函数的返回类型是整型,所以P是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3];//从P开始,先与()结合,说明P是一个函数,然后进入()里面,与int结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的
是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int结合,说明指针指向的
内容是整型数据.所以P是一个参数为一个整数且返回一个指向由整型指针变量组成的数组的指针变量的函数
2.指针的算术运算
有效的指针运算包括相同类型指针之间的赋值运算、指针同整数之间的加法或减法运算、指向相同数组中元素的两个指针间的减法或比较运算、将指针赋
值为0或指针与0之间的比较运算。其它形式的指针运算都是非法的,例如两个指针间的加法、乘法等。
指针可以进行加或减运算,例如:
pnew=pold + n
一个指针 pold加(减)一个整数 n后,结果是一个新的指针pnew,其中pnew和 pold的类型相同及其所指向的类型也相同,pnew的值将比 pold的值增加(减少)
了n乘sizeof(pold所指向的类型)个字节。即pnew=pold+n
即 pnew表示指针pold当前指向的对象之后第n个对象(pold所指类型的对象)的地址。
3. 运算符&和*
&是取地址运算符,*是...书上叫做“间接运算符”(c++叫解引用)。
&n的运算结果是一个指针,指针的类型是n的类型加个*,指针所指向的类型是n的类型,指针所指向的地址,那就是n的地址。
*p 的结果是 p所指向的东西。*p,它的类型是 p指向的类型,它所占用的地址是p所指向的地址。
int n=1;
p=&n;//&n的结果是一个指针,类型是int*,指向的类型是int,指向的地址是n的地址。
4. 指针表达式
一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表达式。
char *str[32];
char **pstr=str;//如果把str看作指针的话,str也是指针表达式
char *str;
str=*pstr;//*pstr是指针表达式
str=*(pstr+1);//*(pstr+1)是指针表达式
由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,
指针指向的内存区,指针自身占据的内存。
5. 数组和指针的关系
数组的数组名其实可以看作一个指针。
定义一个数组int A[]={1,2, 3};
则数A就有了两重含义:
第一,它代表整个数组,它的类型是 int [3];
第二,它是一个常量指针,该指针的类型是int*,该指针指向的类型是 int,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,
该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似 A++的表达式是错误的。
此外数组在作为函数参数时,数组名将蜕化为指针。
例如:
void fun(char arr[12]);
在编译时编译器会当成是:
void fun(char *arr);//你在这个函数中使用sizeof(arr)得到的值是4
6. 指针和结构类型的关系
struct My
{
int a;
int b;
};
假设我们定义了上面的结构体,同时定义结构体的结构对象并初始化,struct My ss={10,20};
那么我们如何通过指针ptr来访问ss的三个成员变量呢?
答案就是,我们先定义一个指向结构对象ss的指针,struct MyStruct *ptr=&ss;然后,使用指向运算符->便可实现对结构对象ss成员的访问。
ptr->a;//或者可以这们(*ptr).a,建议使用前者
ptr->b;
7. 指针和函数的关系
可以把一个指针声明成为一个指向函数的指针,从而通过函数指针调用函数。
例如:
int fun(char *,int);
int (*pfun)(char *,int);
pfun=fun;
int a=(*pfun)("abc", 3);
例中,定义了一个指向函数fun的指