? ? ? ? 数据中最重要的部分,没有之一.?
? ? ? ? 和数据结构紧密相连.建立符合逻辑结构的数据集合必备.
? ? ? ? 指针的内容比较多,尽可能用场景说明,更多需要自己发掘.
? ? ? ? 计算机中所有数据都在内存中存放,他们都拥有自己的"地址",也就是指针.底层有一种机制,可以用访问地址来访问地址中的数据,这就是指针的意义所在.
? ? ? ? 指针的意义是间接访问数据
? ? ? ? 数据类型 * 指针变量名=数据地址?
? ? ? ? 说明:数据地址通常用"&+数据变量名"来表示,数组名自动表示为数组首元素地址
? ? ? ? 当把某个数据的地址赋给指针,也称作指针指向了这个数据
????????和前面声明某数据类型的变量做对比: "类型名 变量名=值",二者含义基本一致.
int a=10;
int *p=&a; /*整型指针p指向整型变量a*/
int b[]={10,20,30};
int *pa=b; /*整型指针pa指向整型数组b*/
? ? ? ? 指针可以指向单个数据,但不常用;? ? ? ? ? ? ? ? ? ? ????????? ?????????????????//前两行
? ? ? ? 指针更多的用途是指向数据集合(包括数组,链表)内元素? ? ? ???????//后两行
? ? ? ? 当使用指针指向了某个数据之后,例如int *pa=b,即可以用*pa来间接访问该地址内数据;同时指针支持加减法,*(pa+1)或者pa[1]表示偏移一个地址后数据的取用
? ? ? ? 比如:已知某整型数组内有n个元素,打印出来
void print_int(int *p,n){ //形参里的"int*"表示这是一个整型指针类型
for(;p<p+n;p++){
printf("%d\n",*p); //"*p"表示指针指向地址的值
}
}
?调用print_int(b,3)或者print_int(pa,3),将上面的数组b[]和元素数量3传入,得到打印结果.
????????---因为C语言是按值传递的,所以传入的是变量或者常量都可以,为形参传入变量时会查找变量? ?对应的常量(值)
注意:
? ? ? 1>? 因为C语言的独特设计,在声明指针类型,以及函数形参类型时,用到的" * "表示这是一种指针类型,其他地方的"*+指针变量名"表示指针指向地址里的值.这是初学者比较容易迷惑的地方,当然我们也没法去质疑语言的设计者为什么要这样设计,我们只需要记住这个规则即可.
? ? ? ?2> 指针的边界需要程序员自己去掌握.
? ? ? ? 按照上面定义的指针pa指向数组b[],以下几种情况成立:
? ? ? ? *pa==10;? *(pa+1)==20; *(pa+2)==30;
? ? ? ? 如果把上述式子都看成条件表达式,他们的值都为true.? ? ?if( *pa==10)相当于if(true)
? ? ? ? 但是:*(pa+3)等于多少? 这个是未定义的,结果是不确定的.所以指针不允许这样使用,称为指针越界访问内存.
? ? ? ? 同样道理,在调用print_int()函数时,事先知道数组里有3个元素,所以传入3没有问题,但是如果传入其他数字,比如4,则会出现意想不到的结果,这也是被禁止的.
? ? ? ? 1>指针必须指明其数据类型,即属于什么类型的指针.
? ? ? ? ?int *p=a;//假设存在a[]这样的数组
? ? ? ? ?指明了指针是属于整型,然后可以间接访问整型数组里的元素. 这一点很好理解,如果脱离了数据元素类型,采用了 *p=&a;? 来定义估计没人看得懂.
当然C语言里有void *这样的指针,但是建议不要用,因为这可能超出了程序员对数据的控制范围
? ? ? ? 2>指针必须指向已存在的数据.
? ? ? ? 书上有一句话:指针在使用前必须初始化.可能有点难以理解,换成这个说法可能会好一些.
? ? ? ? 前面讲到指针的意义在于间接访问数据.如果不把指针指向已存在的数据(已开辟的内存空间),指针不仅没有意义,而且是错误的.
? ? ? ? 3>指针不能指向具体的内存地址(非绝对)
? ? ? ? 指针表示地址,他的值就是指针指向数据在内存中的地址值.类似于0x0036FE96(假设32位机)这样的值.在C中这样的写法是禁止的:
? ? ? ? int *p=(int *)0x0036FE96;? ?//错误写法!禁止!
????? ? 这表示程序员想直接访问内存地址.但内存地址使用是要经过操作系统允许的.芯片把这个权限交给了操作系统.操作系统有自己的区间划分.即使程序员想要查询自己定义的变量放在哪个地址上,返回的也是虚拟内存地址,而非实际物理地址.
????????????int *p=a;//假设存在a[]这样的数组,允许!
? ? ? ? 话说回来,如果要操作硬件,需要将硬件寄存器映射到某一片内存空间上,这时候可以在允许的内存空间上操作.这不在软件程序员讨论范畴.
????????一般情况下,不能直接操作内存地址.特殊情况下可以,所以说不是绝对的
? ? ? ? 4>在函数内部建立指针的临时变量时,小心内存泄漏
? ? ? ? 举例:写一个函数,建立一个整型数组,整数个数由自定义输入,数组内所有元素等于0;
void createArray(int n){
int* p = (int*)malloc(sizeof(int) * n);
for(int i=0;i<n;i++){
p[i]=0;
}
}
? ? ? ? 乍一看这段代码没有问题.但是因为是在函数中临时申请了内存空间,在函数调用完毕后,没有释放相应的内存空间(free),所以造成了内存空间被占用,而且不被发现.这叫做内存空间泄露.
? ? ? ? 但我们可以解决这个问题:既然申请了内存空间,得到了指针.那么返回的指针由定义指针接收,则保持对这部分数据的访问权限.
int * createArray(int n){
int* p = (int*)malloc(sizeof(int) * n);
for(int i=0;i<n;i++){
p[i]=0;
}
return p;
}
? ? ? ? 5>指针可以指空(NULL)?
? ? ? ? 这点和2>并不矛盾,指针可以指空,常用于链表末尾.
? ? ? ? 从前面内容已经看出来,指针和数组(当然也包括链表),指针和数据集合是紧密联系在一起的.一个必须有的概念应该形成:只要有数据集合,必然有指针去读取或修改.?简单梳理一下逻辑:数据集合必然需要遍历(访问其中的单个元素),遍历必然要用到指针.
? ? ? ? 指针和数据集合存在以下关系:
? ? ? ? 1>可以定义多个指针指向同一个地址.
? ? ? ? 例如:
? ? ? ? int a[]={10,20,30};
????????int *p=a;????????//定义指针p指向数组a
? ? ? ? int *q=a; ????????//定义指针q指向数组a
? ? ? ? ?2>可以定义集合内某具体位置的指针.
? ? ? ? int *first=&a[0];//定义指向数组首元素
? ? ? ? int *last=&a[2];//定义指向数组末元素