主要讲解指针的概念 ,由于笔记跨越时间长,前后可能不连贯。
指针 地址 数组之间的差别
1.指针是个量,对应着一块内存区域;
2.指针存储的信息是某个内存单元的地址。
int p=(int )300500是一个指针,p存储的是地址,指针有类型,从哪里开始(0x300500),长度是多少(sizeof(int)=4),从哪里结束(0x300504),得知了类型以后,就知道这片内存数据是如何解析;
而数组在内存中线性顺序排列,数组名是数组的首地址,可以看作是一个常量指针,其他元素的地址就等于首地址+sizeof(类型)*下标,因此我们可以通过指针间接引用数组。
使用下标的方法访问数组
#include
int main(void) {
int a[10];
int *p=a;
for(int i=0;i<10;i++)
{
p[i]=i;
printf("%d\n",a[i]);
}
return 0;
}
a[i]与*(a+i)等价 &a[i]等价于 a+i;
a是第一个元素的首地址,指向int 类型,每次指针相加,相加指针指向的类型的大小。
*(a+i)等价于根据地址a+I 往前读取四个字节,读取内容。
使用指针变量间接访问
#include
int main(void) {
int a[10];
int num=0;
for(int *p=a;p10;p++)
{
*p=num;
num++;
}
for(int i=0;i<10;i++)
{
printf("%d\n",a[i]);
}
return 0;
}
野指针、空指针和空类型指针
指针使用之前必须初始化,如果不初始化,会将内存中的垃圾数据作为地址,对这个地址的访问没有任何意义,甚至程序挂掉。
如果在指针变量声明之初确实不知道该将此指针指向何处,最简单的方式是将其置“0”,C语言中提供了关键字NULL。其基本形式为:类型* 指针变量名;如
int *pNum=NULL;
值为NULL的指针称为空指针,这意味着,指针并不指向任何地址。 在头文件 stdio.h 中,NULL 定义为常量。
void *p 为空类型的指针,可以接受任何类型指针的赋值,可用于保存地址。空类型指针可以转换为任何类型的指针
指向为空的指针为空指针,当释放掉malloc函数分配的内存之后,应该将指针赋值为空,防止反复释放内存出错。
一级指针
一级指针就是最常见的指针,定义为 : 指针类型 *指针变量名=地址,如
int a=0;
int *p=&a; //p是一个指针变量,p可以是任何变量的地址
*p=10;
printf("a的值为%d",a);
这样就完成了一个最简单的指针应用,通过访问所指向对象的地址,修改数据。
指针类型与指针所指向的类型
所谓指针类型,指的是声明指针变量时位于变量名前的“类型*”,而所谓指针所指向的类型,指的是为指针初始化或赋值的变量类型。指针的类型必须要与指针指向的类型一致,不一致,大小不一样,解析方式不一样。
不是同一类型的指针,不可以任意赋值。
int num;
int *a=#
char *b=NULL;
a=b; //这是不合法的,类型不同
int *c=a; //这是合法的,同类型指针的赋值
不同的数据类型,大小不一样(如果强制赋值的话,就会少读取或多读取,内存有很多垃圾0,1),就算大小一样,如int和float大小都为4个字节,解析方式不一样,结果也不一样。
a和c指向num的地址,也就是说,两个指针指向同一个内存单元,对num的任何改动都会影响*a和*c的值,反之亦然。
二级指针
指针变量也是变量,占据一定的内存空间,有地址就可以用一个指针指向它,指向指针的指针,称为二级指针。常用在外挂中改变一级指针的指向。
#include
int main(void) {
int a=8,b=1;
int* pa=&a; //pa指向a,*pa值为8
int* pb=&b; //pb指向b,*pb值为1
int** pp=&pa; //pp是一个二级指针,指向一级指针pa
*pp=pb; //取pp指向地址的内容,即pa的地址,把pb的地址赋值给pa,
*pb=20; //pa和pb均指向b
printf("*pa的值为%d",*pa); //*pa的值为20
return 0;
}
数组指针
指向数组的指针 假设a为数组名,数组类型为int
1维:int *p= a; //前面有足够的解释,不在介绍
2维:int (*p)[10]= a[10];
则定义了一个名为p的指针变量,它可以指向每行有十个整数(即int型)元素的二维数组。p是指向一维数组的指针变量。这句话的理解是首先(*p)[10]是一个指向一维数组的指针变量,意思就是p这个指针是指向一个含有10个元素的数组的,那么p指针每一次加1就相当于把p中存的地址加20(前提是int类型占2个字节,在VC中是占4个字节)。
举个例子:int a[3][3]; int( *p)[3]; p =a; //p=a的意思是把数组a的首地址存放到p中那么p[1]就是a[1][0]的地址,p[1][0]就等于a[1][0],而p[1][2]就等于a[1][2].
指针数组
指针的数组,一个数组里面存放同类型的指针,如 int *(a[10]); 或省略括号 int *a[10];
表明这是一个有10个元素为int *类型的数组。
注意两者之间的定义差别
int (*p)[10]= NULL; //数组指针,先有(*p),表明是指针
int *(a[10]); //指针数组,先有(a[10]),表明是数组
函数指针
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址,也是函数名,称为这个函数的指针。有地址,就可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。例如:
int (*p)(int,int);
定义p是指向函数的指针变量,它可以指向类型为整型且有两个整型参数的函数,即int fun(int x,int y) p的类型用int (*)(int,int)表示,当调用这个函数指针时,传入相应的参数,就会执行这个函数。
函数返回值是指针
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回值的类型是指针类型而已
定义返回指针值的函数的一般形式为 类型名 *函数名(参数表列);
如 void * fun(void); //表明fun()的返回值是void*类型
如果程序中需要函数返回地址,就需要用到返回值是指针的函数。
指针运算
指针++就是按照指针类型的大小,前进一个类型的大小,int前进四个字节
指针 ++ 和 – 只有在数组的内部才有意义。
若有p=a(p指向数组a),则:p++(或p+=1),表示p指向下一元素。
*p++
与*(p++)
等价。同样优先级,结合方向为自右向左。
*(p++)
与*(++p)
前者是先取*p的值,后使p值加1,相当于a[i++];后者是先使p加1,再取*p,相当于a[++i]。
(*p)++
表示p所指向的元素值加1,而非指针值加1
p++
是先引用,再自增,自增一个sizeof(指针指向的类型)的大小。
++指针在数组内部向前移动一个元素的大小
p=p+1; 指针在数组内部向前移动一个元素的大小
*p++
等价于 *(p++)
, ++是先引用再自增,++优先级高于*
指针在数组内部向前移动一个元素的大小
++p 先自增,再引用
(*p)++ 取出指针指向的内容自增一下
指针的加减法在非数组内部没有任何意义,而且很容易越界报错,因为一个exe不能读写其他exe进程的内存。
指针比较
两个毫无关联的指针比较大小没有意义,因为指针只代表了“位置”这么一个信息, 但是 如果两个指针所指向的元素位于同一个数组(或同一块动态申请的内存中), 指针的大小比较反映了元素在数组中的先后关系。
另外 通过指针是否相等,可以判断是否指向同一地址。