一.指针的基本构成
1.指针与地址
通常来讲,指针就是地址,地址就是指针。可以从两种角度来认识它:
1.可以把它看作是一个编号,计算机内的每一块空间都有一个地址,也就是这个编号。
2.可以抽象的把它认为是一个箭头,它代表其所指向空间的地址。
比如说:地址为0x00000012的空间,里面存放了一个整型数据1,单位为四个字节。指针指向或者说代表的就是0x00000012。
2.指针变量
a.基本形式:定义一个 *p,其中p是指针变量名,* 是解引用操作符。
*p的含义:p代表的是一个地址,通过p可以找到存储数据的空间位置,再通过* 可以取出p地址空间里面的内容。
既然提到了*,那么就不能忘了还有&(取地址操作符)。可以通过&,取出变量的地址。
那么如何定义一个指针变量呢?我们以整型指针为例,如下所示:
int a=10;
//定义一个整型a
int *pa;
//再定义一个同样为整型的指针变量
pa=&a;
//右边意为把a的地址取出,然后存进指针变量pa中
int b=*pa;
//如果我们想把a的值赋值给b,就可以通过*,对pa进行解引用操作,通过pa中存储的a的地址找到a空间存储的内容即10
注:*和&两者为互逆运算
b.指针变量类型
指针在计算机中同样以数据形式存储,指针可以存放地址信息,但同样指针自身也有地址和空间。指针类型的不同也影响了指针的运算以及在计算机中的指针的指向。有int*,char*,long*等类型,而不同类型的指针变量所占的字节大小不同,比如int*占4个字节,char*占一个字节。
c.指针变量的初始化和野指针
指针变量也需要初始化,一般来说,需要向里面输入一个地址以明确指针的指向,否则,指针所指向的位置可能会随机,这意味着一些计算机重要的空间可能被访问或修改。就算不给指针变量一个明确的地址,也要给它赋值NULL,不然这就是一个野指针。
3.指针的运算
a.赋值运算:这个较为简单,上面也有所说明
b.加减运算
int*pa;
int a=*pa;
a=*(pa+1);//取a地址后一位地址的值赋给a
pa-pb=两个地址中间的元素个数
c.关系运算
pa>pb
//pa的地址大于pb的地址
pa==pb
//pa的地址等于pb的地址
pa&&pb=!NULL
//pa和pb的地址不为空
二.指针与其他C语言主体的结合
1.指针和数组
a.数组指针 (*p)[ ]
定义(要与数组指针区分开来):一个指向数组的指针,其本质是一个指针,对象是数组,在
(*p)[ ] 中,* 先与指针变量p结合,成为一个指针,再与[ ] 结合,表明指向。
一维数组
int arr[5]={1,2,3,4,5};
//定义一个整型一维数组
int*pa=arr;
//把pa定义为一个数组指针,指向arr数组
pa=&arr[0];
//其中,指针pa指向的是一维数组首元素的地址
指针pa指向的是一维数组首元素的地址
如果想要用指针来访问整个数组的内容,就可以如下:
int arr[]={1,2,3,4,5};
int *pa=arr;
for(int i=0;i<5;i++)
{
printf("%d ",*(pa+i));//通过多次循环就可以用指针访问到数组中的内容了
}
多维数组
这里以二维数组为例,指针pa指向的是二维数组第一行首元素的地址
int arr[2][3]={{1,2,3},{4,5,6}};
int (*pa)[3]=arr;
//二维数组的指针定义较为特殊,列不能省略
//其中pa=&arr[0][0],表示第一行首元素地址
//而(pa+1)=&arr[1][0],表示第二行首元素地址
//而(pa+1)+1=&arr[1][1],表示第二行第二个元素地址
如果要用循环和指针来访问数组的话,需要一个双重循环,跟一维数组的详细,就不多赘述了。
以上都属于数组指针的范畴,接下来介绍指针数组。
b.指针数组 *p[ ]
定义(要与数组指针区分开来):一个元素是指针的数组,其本质是一个数组,元素是指针,在
*p[ ] 中,p 先与[ ]结合,成为一个数组,再与 * 结合,表明其元素。
int a=10;
int b=5;
int* pa=&a;//int先和*结合,表明指针类型,pa为指针变量,存储从a中&的地址
int* pb=&b;
//先定义两个整型指针
int *arr[]={pa,pb};
//再定义一个指针数组把指向a和b的指针存储起来
2.指针和字符串
这个没什么好说的,直接看例子
char* str={"i am a student"};
printf("%s",str);
//这里的str指的是str字符串的字符首地址,即'i'的地址
3.指针和函数
函数指针基本形式是:函数返回类型+ * +指针变量名+(参数1,参数2),比如
int* pt (int,int)
同时指针也可以作为函数的参数,比如
int* pt (void*,void*)
而且,函数指针和数组连用可以起到提高函数利用效率的作用
int Add(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
//先定义三个不同功能的函数
int(*pfarr[])(int, int) = {NULL,Add,Sub,MUL};//这里加NULL是为了添空,让k跟元素下标能对应上
//再定义一个函数指针数组,把定义好的函数作为*pfarr数组的元素
ret = (*pfarr[k])(a, b);
//通过改变k的值,改变指针指向,进而调用不同函数
4.多级指针
因为上面讲到指针本身也有地址,所以指针也可以被其他指针所存储。这里以二级指针为例
int a=10;
int* p=&a;
//这是一级指针
int** pp=&p;
//这是二级指针,*pp里存放了p的地址
int ret=**pp;
//这样就可读取到a的值了
多级指针可以让指针的运用更灵活,但一般到二级指针就够用了,再多可能会更复杂
5.指针和结构体
代码如下
struct stu
{
char name[20];
int age;
int weight;
};//先定义一个结构体
struct stu student1 = {"gmy",18,140};
//完善结构体内容
struct stu *p = &student1;
//定义结构体指针,指向student1
printf("%s\n", p->name);//或者p.name
printf("%d\n", p->age);
printf("%d\n", p->weight);