------ 知之博客 期待与您交流! -------
一 指针的概念
内存单元的编号也叫做地址。既然根据内存单元的编号可以找到所需的内存单元,所以你通常也把这个地址称为指针。
二 指针存在的价值
1).为函数提供修改调用变量的灵活手段;
2).让函数有多个返回值;
3).可以改善某些程序的效率;
4).为动态数据结构(如二叉树,链表)提供支持。
三 指针变量
c语言中,允许用一个变量存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某个内存单元的指针。
注:指针是地址,是一个常量;指针变量是存放一个地址,是一个变量。
四 指针变量的定义
1).指针类型说明,即定义变量为一个指针变量;
2).指针变量名
3).变量值(指针)
一般形式:
类型说明符 *变量名;
例如: int * p ; //一个指向整型变量的指针
long * p ; //一个指向长整型变量的指针
float * p ; //一个指向浮点型变量的指针
* 表示定义了一个指针变量
一个类型的指针只能指向同类型的变量,不能指向其他类型的变量
指针也可以被声明为全局 、静态局部和局部的。
如果一个指针变量定义以后,如果不进行初始化,这个指针变量存放的是一个垃圾数,这个指针变量称为野指针。
五 指针的应用场景
1)在被调函数中可以修改主调函数中的变量的值
2)让函数有多个返回值
例如:
#include <stdio.h>
void yunsuan(int x,int y,int *he,int *cha) {
*he = x + y;<span style="white-space:pre"> </span>//计算两个数的和
*cha = x - y;<span style="white-space:pre"> </span>//计算两个数的差
}
int main(int argc, const char * argv[]) {
int he = 0,cha = 0;
yunsuan(2, 3, &he,&cha);
printf("he = %d\ncha = %d\n",he,cha);
return 0;
}
例子中函数yun suan并没有返回值,却借助指针修改了指针指向的内存空间的数值,可以看作是无形之中添加了返回值。
二级指针以及多级指针在iOS开发中运用较少,简单的介绍一下。
二级指针:如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量,也成为“二级指针”。多级指针以此类推。接下来用一个图简单了介绍一下二级指针和一级指针的区别。
六 指针的类型
简单地讲,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。
例如:
(1) int * p ; //指针的类型是 int* (2)char*p; //指针的类型是 char* (3)int**p; //指针的类型是 int** (4)int(*p)[3]; //指针的类型是int(*)[3] (5)int*(*p)[4]; //指针的类型是int*(*)
七 指针指向的类型
当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
例如:
(1)int*p; //指针所指向的内容是int (2)char*p; //指针所指向的内容是char (3)int**p; //指针所指向的内容是int* (4)int(*p)[3]; <span style="white-space:pre"> </span>//指针所指向的的类型是int()[3] (5)int*(*p)[4];<span style="white-space:pre"> </span>//指针所指向的的类型是int*()[4]
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在 32 位平台里,指针本身占据了 4个字节的长度。
八 指针的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。
例如:
char a[10]; //定义一个char类型的一维数组 int *p = (int*)a; //定义一个指向整型变量的指针p,强制转换并不会改变a的类型 p++; <span style="white-space:pre"> </span> //指针指向下一个单元
指针 p 的类型是 int*,它指向的类型是 int,它被初始化为指向整型变量 a。第 3 句中,指针 p 被加了 1,编译器处理的方式:将指针 p 的值加上sizeof(int),在 32 位程序中,是被加上了4,因为在 32 位程序中,int 占 4 个字节。由于地址是用字节做单位的, 故 p 所指向的地址由原来的变量 a 的地址向高地址方向增加了 4 个字节。由于 char 类型的长度是一个字节,所以,原来 p 是指向数组 a 的第 0 号单元开始的四个字节,此时指向了数组 a 中从第 4 号单元开始的四个字节。
注意:两个指针不能进行加法运算,属于非法操作,因为进行加法后,得到的 结果指向一个不知所向的地方,并且没有实际意义。但是两个指针可以进行减法操作,但必须类型相同,一般用在数组方面。
九 指针与数组
说到数组的概念如果不理解的话,可以参阅我的文章《黑马程序员-iOS基础(c)-数组》。首先来看一个例子:
例如:
int array[5]={0,1,2,3,4,},value; ... ... value = array[0]; //也可写成:value=*array; value = array[3]; //也可写成:value=*(array+3); value = array[4]; //也可写成:value=*(array+4);
由此可见,数组名array代表数组本身,类型是int [5]。但如果把array看做指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。因此数组的数组名可以看作一个指针。
sizeof(指针名称)测出的是指针自身类型的大小呢还是指针所指向的类型的大小?来看一个例子。
例如:
int (*p)[10]; 则在32位程序中,有: sizeof(int(*)[10])== 4 sizeof(int[10]) == 40 sizeof(p) == 4
可以看出sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
十 指针和结构体
首先来定义一个结构体:
例如:
structMyStruct { int a; int b; int c; }; struct MyStruct ss={20,30,40}; //声明了结构对象 ss,并把 ss 的成员初始化为 20,30和40。 struct MyStruct *ptr=&ss; //声明了一个指向结构对象ss 的指针。它的类型是 MyStruct *,它指向的类型是 MyStruct。 int *pstr=(int*)&ss; //声明了一个指向结构对象ss 的指针。但是 pstr 和 //它被指向的类型 ptr 是不同的。
怎样通过指针 ptr 来访问 ss 的三个成员变量?
答案:
ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者 ptr->b; ptr->c;
怎样通过指针 pstr 来访问 ss 的三个成员变量?
答案:
*pstr; //访问了 ss 的成员 a。 *(pstr+1); //访问了 ss 的成员 b。 *(pstr+2) //访问了 ss 的成员 c。
以上例子中,即使*pstr 访问到了结构对象 ss 的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员 a 和成员 b 之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节。这也间接地证明了指针的灵活性。
十一 指针和函数
对于函数的了解可以查看我的文章《黑马培训-iOS基础(c)-函数》,文章对函数做了详细的介绍。还是首先来看一个例子:
例如:
int fun(char*); int a; charstr[]="abcdefghijklmn"; a=fun(str); int fun(char*s) { int num=0; for(int i=0;;) { num+=*s; s++; } return num; }
本例中的函数 fun 统计一个字符串中各个字符的 ASCII 码值之和。数组的名字也是一个指针。在函数调用中,当把 str 作为实参传递给形参 s 后,实际是把 str 的值传递给了 s,s 所指向的地址,就和 str 所指向的地址一致,但是 str 和 s 各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。
总结:可以把一个指针声明成为一个指向函数的指针,也可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
在使用指针时,程序员心里必须非 常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。