文章目录
1.什么是指针?
指针即地址。指针是个变量是用来存放变量内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的(32位机器为4个字节64位机器为8个字节),而存放数据的变量因数据的类型不同,所占用的空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
我们该如何理解它呢?
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;//取出变量a的地址,使用&操作符。
//将a的地址存放在p变量中,p就是一个指针变量。
*p = 0;//对指针进行解引用的操作,对这块内存中的内容进行修改
return 0;
}
2.指针类型
2.1整型指针
int a = 0; //p是一个整型指针变量,其存放的是a的地址
int* p = &a;
2.2字符指针
字符指针,即指向一个数组的指针
定义方式:
方式一:
char c = 'a';
char* pc = &c;
方式二:
char c = "hello world";
char* pc = &c; //这种方式是将字符串c的首元素的地址存放在指针pc中
2.3数组指针
数组指针,即指向一个数组的指针。
定义方式:
int arr[5] = {0};
int (*p)[5] = &arr; //p是指针,其指向数组arr的地址,指针指向的类型是一个有5个元素的整型数组
指针数组
指针数组,即元素为指针的数组(这里介绍时为了区分数组指针与指针数组)
定义方式:
int a = 0;
int b = 0;
int c = 0;
int* arr[3] = {&a,&b,&c};
2.4函数指针
函数指针,即指针指向一个函数的地址
定义方式:
int fun(int a) //定义一个函数
{
return a;
}
int (*f)(int a); //定义一个函数指针
f=&fun;
int ret = f(a);//可通过函数指针去调用该函数
int z = (*f)(a);
需要注意的地方:上述代码在定义函数指针的时候“*f”加了小括号 注意优先级的问题,如果没有加()那么时定义了一个返回值为整型指针的函数
我们来看两段代码来感受一下函数指针
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
看代码1:
( void( * )( ) ) ,我们先看这部分,这括号内部分不难看出这是一个函数指针类型,接着是( void( * )( ) )0 结合0看是将0强制转换成 这个函数指针类型(这个函数是无返回值,无参数),转换后0是一个函数指针,将0解引用操作后找到这个函数的地址调用这个函数。
看代码2:
signal( int , void(*)(int) ),我们先看这部分,这也不难看出这是一个函数,其参数是int类型,和函数指针类型(这个指针所指向的这个函数无返回值,参数是int类型),接下来看外面的部分
void(*)(int);
外面的部分很明显是一个函数指针类型(这个指针所指向的这个函数无返回值,参数是int类型),这个就是函数signal( int , void(*)(int) )的返回值类型,也就是说这个代码是一个函数的声明,它的返回值是一个函数指针,参数是int类型,和函数指针类型。
这里呢,可能有一些朋友会对返回值类型这里有些不能理解,那么我们来另一种理解方式:
typedef void(*pf)(int);
pf signal(int, pf);
这里我们使用typedef将这个函数指针类型进行了重新命名,那么这个指针pf就是上述的函数指针
pf signal(int,pf);
这个是转换后的代码,这个代码更容易让大家理解。
2.4.1函数指针数组
函数指针数组,即数组的元素类型为函数指针。
定义方式:
int (*pf[5])();
我们该如何理解它呢?
首先,pf先与[]结合说明pf是一个数组,再看int (*)();这个是一个函数指针类型,数组内存放的类型是函数指针类型。
2.4.2指向函数指针数组的指针
这是一个指针,指向的一个数组,这个数组内存放的类型是函数指针类型。
定义方式:
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pf
void (*pf)(const char*) = test;
//函数指针数组pfArr
void (*pfArr[5])(const char* str);
pfArr[0] = test;
//指向函数指针数组pfArr的指针ppfArr
void (*(*ppfArr)[10])(const char*) = &pfArr;
return 0;
}
如何理解呢?
先看 (*ppfArr) * 先和ppfArr结合,说明这是个指针,接着是[10],说明这个指针指向的是一个数组,再看最后外面的这一部分,
void(*)( const char *)这是个函数指针类型,即这个数组中存放的类型就是这个函数指针类型。
3.野指针
野指针,即指针所指向的位置是不确定的,是随机的,程序是不能控制的。
产生的原因:
1.局部指针变量没有初始化。
2.在用指针访问数组时,越界访问。
3.动态内存分配,指针在释放后没有置空。
规避野指针的方法:
在定义局部指针变量时,如果你不知道开始要指向谁,一定要让其指向一个空指针。
动态内存分配中在释放一个指针后,要将其置空。
4.指针运算
4.1指针的算术运算
来看一段代码:
int main()
{
int a = 0;
char b = 0;
int* p1 = &a;
char* p2 = &b;
printf("p1=%p,p2=%p\n", p1, p2);
p1++;
p2++;
printf("p1=%p,p2=%p", p1, p2);
return 0;
}
一个指针加减整数后,其类型决定了它移动多少个字节
4.2指针的关系运算
for(p = &arr[5]; p > &arr[0];)
{
*--p = 0;
}
上述代码通过指针的关系运算来进行的,指针也可以进行比较大小,使用指针将一个数组的前5个元素都赋值为0。
5.指针与数组的关系
数组名,即数组首元素的地址,我们可以用一个指针变量来接受这个地址,通过指针来访问数组成员。
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* p = arr;
printf("p=%p &arr[0]=%p\n", p, &arr[0]);
for (int i = 0; i < 5; i++)
{
printf("%d\n", *(p++));
}
return 0;
}
这次的指针讲解就是以上这些,后续会更新更多的内容,如果有哪里写的有问题,希望大家可以指正。