说到C语言,指针绝对是c语言的压轴出场,是c语言的重头戏,也是C语言的精华所在,由于指针的存在,我们可以直接对内存空间操作,拿到某个变量的指针(得到它的地址),那么它那块空间就属于我的了,想干什么就干什么!!!!学习C和C++的同学肯定不会对指针感到陌生,这确实是个让我们又爱又恨的东西。
不曾忘记,因为指针操作引起的内存泄露、段错误而彻夜难眠;
也不曾忘记,因为指针的灵活和强大,让我们自由地游刃在内存之中。
就好像c++里由于虚函数的存在,出来一个虚表,那么只要拿到这个虚表的指针(虚表的地址就是该对象的首地址),那么对于父类和子类的无论私有还是保护的成员函数,我们都可以拿来用,因为我们已经越过了private或者protected的防盗网,直接进入了仓库,所以使得累的封装和安全性受到了威胁!所以指针确实是个很强大的东西,这是我的一点儿小体会,可能不太准确,见者海涵!下面来说说指针与一维数组!!!
指针:指针是一个装地址的变量;
内存地址:每一个字节都有唯一的标识符;
地址云算符:&变量名;
间接运算符:*地址;
注意:
1.不要对未初始化的指针操作;
2.对于堆内存分配的空间,在delete或者是free()后,一定要给指针赋空值;防止野指针的产生;
下面上一段代码示例:
#include <stdio.h>
int main()
{
int a=10;
int *p=&a;
int **pp=&p;
//pp是一个指针,指向的是一个int*(装的是一个指针的地址,该指针指向int);目的是为了能间接对a空间操作;
// (&pp)++; //地址不能++;=号左边要求是一个变量;
//p++;pp++; //因为p和pp都是变量,所以可以++;
int *******************************p1;//这个编译都没有问题,p1是一个指针变量;
//其实还可以继续定义:
int ***p2=&pp;
int ****p3=&p2;
printf("%p\n",&a); //a的地址;
printf("%p\n",&p); //指针变量p的地址;
printf("%p\n",p); //p指向的地址,指向的a的地址;p空间里存的是a的地址;
printf("%d\n",*p); //p这个空间,里面装的是10;
printf("%p\n",&pp); //二级指针变量的地址;
printf("%p\n",pp); //指向的是p的地址;
printf("%p\n",*pp); //对p的空间的间接引用;执行读,取,写操作;
printf("%d\n",**pp);//对p指向的空间,即a所在空间的间接引用,里面装的是10;
return 0;
}
指针就是一个装着某种类型地址的变量;
注意:指针类型的+-,都是+-指针对应的字节数;
int *p=NULL;p++ +4;
double *p=NULL;p++ +8;
#include <stdio.h>
int main()
{
int as[4]={1,2,3,4};
double *p=(double *)as;
p++;
printf("%d\n",*p);//3
return 0;
}
数组其实是一种比较特殊的数据类型,它所分配的内存空间是连续的,查找效率比较高,但是删除和插入较链表就显得比较笨拙低效;
上一段代码示例:
#include <stdio.h>
int main()
{
int a[3]={11,22,33};
printf("%p\n",&a); //0012ff3c
printf("%p\n",a); //0012ff3c
printf("%p\n",&a[0]);//0012ff3c
/*
a==&a
a==&a[0]
*a==a[0] *(a+2)==a[3]==*(*(&a+1)-1)
一定要注意类型的大小;
*/
printf("%d\n",sizeof(&a)); //12---内存地址;类型为4*sizeof(int )
printf("%d\n",sizeof(a)); //12---内存空间名;
printf("%d\n",sizeof(a[0]));//4
printf("%d\n",*a); //11
printf("%d\n",*(a+2)); //33
printf("%d\n",*(*(&a+1)-1)); //33
return 0;
}
这里想着重说一下sizeof(&a)和sizeof(a);a是一维数组的名字
代码的实验大部分都是在VC6.0上实现的,由于个人习惯原因,虽然该编译器比较老,但是可能刚接触的就是它,习惯了吧!!!这样也导致了部分实验的结果可能因为编译器的不同而不同,若有错,欢迎指正,望海涵!
因为这两句话我思考了好久,也看了一些资料:
最后发现也许是VC6.0比较老的缘故,它的sizeof(&a),在VS2005上运行的时候结果是:4.而VC6上的结果是12;然后sizeof(a)都是12,没问题!
我们知道a=&a[0]是int *类型的;
&a是int *[n]类型的,n代表的是元素的个数;
因为我在VS2005上运行的结果如下:
#include <iostream>
using namespace std;
int main()
{
int a[3]={1,2,3};
int (*p)[3]=&a;
cout<<**p<<endl; //1
cout<<*(*(p+1)-1)<<endl;//3
cout<<*(*p+1)<<endl; //2
cout<<*(*(&a+1)-1)<<endl;//3
cout<<sizeof(&a)<<endl; //4
cout<<sizeof(a)<<endl; //12
system("pause");
return 0;
}
我看过陈正冲的《C语言深度解剖》,这本书还不错,里面第四章指针与数组讲到(*p)[N]=&a;那么p=&a;
sizeof(p)=sizeof(&a)就应该=4;就是一个地址而已,只要是地址,其大小就是4字节(32位)。
BUT
因为VC6中的sizeof(p)=4,sizeof(&a)=12;
可是在VS2005中sizeof(&a)=4,但是其类型还是int (*)[N],类型的,并不是int *类型的,因为上面的代码也有体现:
*(*(&a+1)-1)=*(*(p+1)-1)=3;
和VS6中的一样,这样看来,VS6中的sizeof(&a)又有它一定的道理,应该是整个数组的空间大小12而不是4!可能是因为数组名确实是个比较特殊的东西吧!
因为数组名本质上是个地址,但不能说是指针,它能给指针赋值,是因为,指针本身的数据结构和数组名(地址)是一样的 都是32位的int,所以这里能通过指针来对数组进行操作; 因为它本身是地址的属性,和指针就可以相互赋值,打通了友好往来的桥梁!!用起来也非常方便!
注意:地址不能++;
例如:
int a=10;
a++;//ok;
++a++;//error,a++是个常数11;不能再++了;
int as[3]={1,2,3};
as++;//error;地址不能++;
as=as+1;//error ,错误同上;
as+1;//OK..这个是可以的;它不是赋值,而是一个地址的移动;这是可以的!没有该变地址as的值,它的值是不允许改变的;
本人愚钝,领悟至此,颇有感慨,与己共勉,陋文浅显,见者海涵。