C语言指针应用
一、指针是什么
指针是一个特殊的变量,它存的是内存里的一个地址。
指针四要素:
1.指针的类型
把指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型如下:
(1)int * p;//指针的类型是int *
(2)char * p;//指针的类型是char *
(3)int * * p;//指针的类型是int * *
(4)int( * p)[3];//指针的类型是int( * )[3]
(5)int * ( * p)[4];//指针的类型是int * ( * )[4]
2.指针所指向的类型
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符 * 去掉,剩下的就是指针所指向的类型。例如:
(1)int * p; //指针所指向的类型是int
(2)char * p; //指针所指向的的类型是char
(3)int * * p; //指针所指向的的类型是int *
(4)int( * p)[3]; //指针所指向的的类型是int()[3]
(5)int * ( * p)[4]; //指针所指向的的类型是int * ()[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
3.指针的值----或者叫指针所指向的内存区或地址
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。
如果说指针的值是a,就相当于说该指针指向了以a为首地址的一片内存区域;说指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
每看代码每遇到一个指针,都应该想到:这个指针的类型?指针指的类型?该指针指向了哪里?(重点注意)
4 指针本身所占据的内存区
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
二、指针的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。例如:
例:
char a[20];
int * ptr=(int * )a; //强制类型转换并不会改变a 的类型
ptr++;
在上例中,指针ptr 的类型是int * ,它指向的类型是int,它被初始化为指向整型变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。我们可以用一个指针和一个循环来遍历一个数组,看例子:
例:
int array[20]={6};
int * ptr=array;
for(i=0;i<20;i++)
{
( * ptr)++;
ptr++;
}
这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1 个单元,所以每次循环都能访问数组的下一个单元。
总结一下:
一个指针ptrold 加(减)一个整数n 后,结果是一个新的指针ptrnew,ptrnew 的类型和ptrold 的类型相同,ptrnew 所指向的类型和ptrold所指向的类型也相同。ptrnew 的值将比ptrold 的值增加(减少)了n 乘sizeof(ptrold 所指向的类型)个字节。就是说,ptrnew 所指向的内存区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘sizeof(ptrold 所指向的类型)个字节。
三、运算符&和 *
这里&是取地址运算符, * 是间接运算符。
&a 的运算结果是一个指针,指针的类型是a ,指针所指向的类型是a 的类型,指针所指向的地址嘛,那就是a 的地址。
- p 的结果是p 所指向的东西,这个东西的类型是p 指向的类型,它所占用的地址是p所指向的地址。
四、指针表达式
一个表达式的结果如果是一个指针,那么这个表达式就叫指针表式。
下面是一些指针表达式的例子:
例:
int a;
int * pa;
pa=&a; //&a 是一个指针表达式。
Int * * ptr=&pa; //&pa 也是一个指针表达式。
当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。在例七中,&a 不是一个左值,因为它还没有占据明确的内存。
五、数组和指针的关系
数组的数组名其实可以看作一个指针。看下例:
例:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0]; //也可写成:value= * array;
value=array[3]; //也可写成:value= * (array+3);
value=array[4]; //也可写成:value= * (array+4);
数组指针:指向数组的指针
例:
int ( * p)[10] p 是指向数组的指针
指针数组: 数组的元素都是指针。
例:
char * str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char * * ,它指向的类型是char * 。
六、指针和结构类型的关系
可以声明一个指向结构类型对象的指针。
例:
struct MyStruct
{
int a;
int b;
int c;
};
struct MyStruct ss={20,30,40}; //声明了结构对象ss,并把ss 的成员初始化为20,30 和40。
struct MyStruct * ptr=&ss; //声明了一个指向结构对象ss 的指针。它的类型是
//MyStruct * ,它指向的类型是MyStruct
通过指针ptr 来访问ss 的三个成员变量?
答案:
ptr->a; //指向运算符,或者可以这们( * ptr).a,建议使用前者
ptr->b;
ptr->c;
七、指针和函数的关系
函数指针:
其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:类型说明符 ( * 函数名) (参数)
如下:
int ( * fun)(int x,int y);
函数指针是需要把一个函数的地址赋值给它,有两种写法:
fun = &Function;
fun = Function;
指针函数:
就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式为: * 类型标识符 函数名(参数表)
看看下面这个函数声明:
int fun(int x,int y);
这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
接着看下面这个函数声明:
int * fun(int x,int y);
这和上面那个函数唯一的区别就是在函数名前面多了一个 * 号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针,是一个地址。
指针函数的写法
int * fun(int x,int y);
int * fun(int x,int y);
int * fun(int x,int y);
八、指针的安全问题
注意指针四要素要符合。