一.理解
1.理解一级指针,二级指针
地址--通过给内存单元编号,能快速找到所需要的数据。在c语言里我们把地址称为指针,用一个变量来接收该地址,这个变量就称为指针变量。我们创建变量其实是向内存申请一块空间,指针变量就是把某个地址传给变量。当然指针变量也是变量,是变量就得有地址,那这个就是二级指针。
2.&,*操作符
&是取地址操作符,比如创建一个整型变量int a;&a就是取出变量a的地址,把这样的地址放在哪里呢?答案是:指针变量中。*p表示变量p是指针变量,int *p表示该指针变量存放的地址中的内容是整形int。
*用来定义指针变量,以及解引用
定义一级指针变量int *p,二级指针变量int * * f=&p;
解引用---我们只要拿到了地址,就可以通过地址来访问该地址存储的内容。就如int a=100;int *p=&a;int * *f=&p;对指针p进行解引用(*p),这就意味着我们找到了p所指向的地址的元素内容,也就是变量a;对指针f进行解引用,我们就拿到了指针p的地址,进行解引用(*f),就得到了指针p所指变量的地址,对该地址进行解引用*(*f),就得到了指针p所指的变量的内容。
3.指针变量大小
指针变量的大小,指针变量是存放元素地址的,在32位机器下有32位地址总线,把32位地址总线产生的二进制序列当作一个地址,一个地址就是32个bit,8个bit位为1个字节,那一个地址就是4个字节。如果是64为机器,同理有64个bit位,需要8个字节。
指针变量要接收地址,该地址是4/8个字节。我们就知道了指针大小与类型无关,那指针的类型,形如char *,int *等有啥用呢?
4.指针类型的意义
我们已经知道了,只要是指针变量,字节数就是4/8,那为什么还要有一系列的指针类型?
因为指针类型决定了我们对指针进行解引用的权限(访问的字节数)。比如
int main()
{
int n = 0x11223344;//4个字节
int* p = &n;
*p = 0;
return 0;
}
int main()
{
int n = 0x11223344;//4个字节
char* p = &n;
*p = 0;
return 0;
}
char*类型的通过指针p只改变一个字节,而int*类型的改变了4个字节。
5.指针的+-整数
指针即地址,不同指针类型+1,-1之后跳过的字节数不同,由指针类型决定
int *类型的指针指向的内容位int型,+1后跳过4个字节。char*类型的指针指向的变量的内容为char型,+1后跳过1个字节。但如果是void*类型的指针,它可以接受任意类型的地址,由于所指向的内容的类型不知道,+1后不能确定跳过几个字节,不能进行+-整数的运算以及解引用(不知道访问几个字节)。
6.const修饰指针变量
两种方式,在*前修饰,或者在*后修饰
int const *p,意味着*p不能被修改, 修饰的是指针变量p所指向的内容,保证了指针指向的内容不能被修改为其他值。
int * const p,意味着p不能被修改,修饰的是指针变量p本身,保证了指针变量不能再指向其他地址。
7.野指针,即指针指向的地址的所在内容可能不存在。
8.指针有啥用?传值调用与传址调用
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a, b;
scanf("%d%d", & a, &b);
swap(a, b);
return 0;
}
在swap函数中,形参x,y在栈区被创建,有自己的空间,在swap函数内部实现交换,出了函数就被销毁,不能实现实参的交换。
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a, b;
scanf("%d%d", & a, &b);
swap(&a, &b);
return 0;
}
x,y是指针变量,在函数swap不会单独创建空间,在swap函数内直接通过地址找到主函数中要交换的两个值,a,b,能实现实参的交换。
所以如果想要通过函数修改主调函数中变量的值,可以通过传址。如果只需要通过函数来返回某个想要计算的值,可以通过传值调用。
二.指针与数组,函数的关系
1.数组指针:
是指针,该指针指向数组。(*p)说明p是指针,(*p)[]说明指针p指向数组,int (*p)[]说明指针p指向的数组中的元素类型是int类型的。
2.字符指针:
是指针,该指针指向字符,字符串。类型是char *p.
char *p="hello";这个是吧字符的第一个首地址传入到指针变量p当中。
3.指针数组:
是数组,该数组中每一个元素类型都是指针类型。存放指针的数组。p[]说明p是一个数组,int* p[]说明该数组内的元素类型是int *的。是指针类型的。
4.函数指针:
是指针,该指针指向一个函数的地址,不过与前两个不同的是,函数的地址就是函数名。函数指针的类型是:int (*p)(int,int).(*p)表示p是指针变量,(*p)(int,int)表示两个形参是int类型的,int (*p)(int,int),表示返回值也是int类型的。函数指针变量的用途:通过函数指针调用指针指向的函数。如,int(*p)(int,int)=add;int tmp=(*p)(x,y).
5.函数指针数组:
是数组,该数组的每一个元素都是函数指针类型的。函数指针数组的类型:int(*)()p[],p[]表示p是一个数组,int(*)()p[]表示每个元素是int(*)()类型的,但是这样写不合法,改成这个样子:int(*p[])()。函数指针数组用途:转移表,如int tmp=(*p[1])(x,y).
三.一维数组
1.一维数组的数组名:
数组名是元素的首元素地址(除了两个例外),首元素地址怎么理解?比如该数组内元素每个都是整形,整形有四个字节,有四个地址,这里指第一个字节的地址。
两个例外:1.&(数组名),这里的数组是指整个数组,取出的是整个数组的地址,如果数组内元素是整形,那么整个数组的类型是int(*)[]
2.sizeof(数组名),这里也指整个数组,算出的是整个数组的所占的字节数
整个数组的地址与数组首元素的地址不一样!!!
整个数组的地址可以写成&arr,类型是int(*)[]。数组首元素地址可以写成arr,类型是 int *。
2.一维数组传参的本质
一维数组的数组名是元素首元素的地址,我们传递参数本质上是传入的地址,那形参本就该用指针来接收这个地址,用int *p=array来接收。
所以形参可以使用数组的形式,也可使用指针的形式。
四.二维数组:
每个元素是一个一维数组。
1.二维数组的数组名:
数组名是首元素的地址(两个例外),第一个元素是一维数组,那该地址就是一维数组的地址,类型是数组指针类型。该指针指向一维数组。
2.二维数组传参的本质
二维数组的数组名是一维数组的地址,形参也用指针接收,指针类型是数组指针,用 int (*p)[]来接收。int (*p)[]=&array.
所以形参可以使用数组的形式,也可使用指针的形式。