【C语言】C指针详解(含思维导图)


在这里插入图片描述

一、什么是指针

用生活中的房子来举例,我们自己就像一个个数据,房子就是这些数据在的空间,而指针就是房子地址(房间号),也是钥匙用来找到和访问这些数据。

二、 指针变量和操作符

2.1 指针变量的定义

一般定义形式是,*数据类型 * 指针变量名 = &变量名;*变量名是指针要指向的变量的名字。(特殊形式在下文指针分类中详细讲解)。
举个整形指针的例子

int a = 10;
int* pf = &a;

此时的pf就是一个指向a的指针。

2.2 操作符

2.2.1 取地址操作符(&)

已知指针指向的是一块数据的地址,那将变量地址拿出来就需要用到取地址操作符(&),使用形式就是 &变量名。

2.2.2 解引用操作符(*)

我们创建一个指针就是为了更方便的使用变量,那我们如何通过指针来对变量进行操作呢?就需要解引用操作符(*);
使用形式就是 *指针变量名

int a = 10;
int* pf = &a;
*pf = 0;

通过上面的代码就将变量a中的值改为了0.

三、指针分类及使用

3.1 整型/浮点型/布尔类型指针/字符指针

这几种类型的指针定义都是一样的,*数据类型 * 指针变量名 = &变量名;只要有这写类型就可以创建相应指针类型。

//整型指针
short int sa = 1;//短整型
short int* psa = &sa;//短整型指针

int a = 2;//整型
int* pa = &a;//整形指针

long int la = 3;//长整型
long int* pla = &la;//长整型指针

long long int lla = 4;//长长整型
long long int plla = &lla;//长长整形指针
//浮点型指针
float f = 1.0f;//单精度浮点型
float* pf = &f;//单精度浮点型指针

double df = 2.0;//双精度浮点型
double* pdf = &df;//双精度浮点型指针

long double ldf = 3.0;
long double* pldf = &ldf;

//布尔类型指针
bool flag = true;//布尔类型
bool* pflag = &flag;//布尔类型指针

//字符变量指针
char ch = 'a';//字符变量
char* pch = &ch;//字符变量指针

3.1.1 使用

3.1.1.1 函数传参

当函数中参数是指针是就可以将指针传过去,那有些人就可能会说这不是多此一举吗?传指针为什么不直接传变量过去呢?假如我们需要将实参传过去后实参需要加1:

void a1(int x, int y)
{
	x++;
	y++;
}
void a2(int* x, int* y)
{
	(*x)++;
	(*y)++;
}

这两个函数哪一种会成功呢?在函数的学习中我们就知道形参的改变不会影响实参。但当我们传的是指针时,对指针进行解引用操作拿到的是实参的地址,对该地址操作就会把实参改变。所以第二种会成功,第一种会失败。

3.1.1.2 取得变量/变量修改

对指针解引用就可以取得变量。
直接使用指针对变量进行修改。

int a = 10;
int* pf = &a;
printf("%d ",*pf);
printf("%d ",a);//两种是一个效果
*pf = 0;

3.2 数组指针

在介绍数组指针之前我们先来介绍数组名与数组首元素的区别。行数组指针定义在二维数组指针使用中讲解。

3.2.1 数组名与数组首元素

在数组中首元素与数组名取得的地址(都用&操作符取地址)是同一块,但是意义却不一样。

int arr[10] = { 0 };
int* pa1 = &arr[0];
int* pa2 = &arr;
int* pa3 = arr;

首元素地址下一块是下一个数组元素地址,而数组名地址下一块是将整个数组跳过。 也就是说pa2 指向的是一整块数组并不能单独对数组成员进行访问。
那c中就只有两种指向整个数组的情况

  1. &数组名
  2. sizeof(数组名),sizeof操作符对数组求长度就是对整个数组求长度。
    那pa3定义的指针是如何作用呢?其实pa3与pa1是完全相同的。数组名本身就是数组首元素地址。

3.2.2 定义

根据实际情况看需要的数组指针是需要指向整个数组还是数组元素,
数组类型* 指针名 = 数组名;

3.2.3 数组指针与指针数组的区别

介绍了数组指针就不得不提一下指针数组了,刚开始接触可能是有点傻傻分不清,但是只要加个的就可以搞清了。**数组指针就是指向数组的指针;指针数组就是储存指针变量的数组。**一定要将它们的本质(就是最后两个字)区分开,数组指针是指针,指针数组是数组。

3.2.4 数组指针的使用

3.2.4.1 一维数组指针使用
3.2.4.1.1 一维数组指针传参

当一个函数中对数组操作的参数是数组指针时传参。我们以一个打印数组元素为例

void print(int* arr,size_t len)//len表示数组长度
{
	for(int i = 0; i < len; i++)
	{
		printf("%d ",arr[i]);
		printf("%d ",*(arr+i) );
	}
}

以上的两种printf()函数使用都是同一个效果。

3.2.4.1.2 一维数组指针取变量/改变量

要取得数组中的第几个元素(0开始)就直接让指针加几就行或者跟数组使用相同。例如要将数组中的第9个元素改值。

int arr[10] = { 0 };
int* pa = arr;
*(arr+9) = 1;
arr[9] = 1;//相同效果
3.2.4.2 二维数组指针使用
3.2.4.3 多维数组指针和行指针定义

我们还是像前面定义数组指针时那样定义

int arr[3][3] = { {1,2,3},{1,2,3},{1,2,3} };
int *pa = arr;

此时的arr代表的是第一行的地址,也就是说pa指向的是一个数组而不是一个元素。那我们如何拿到具体的元素呢?pa[行]+第几个;或者像二维数组使用一样;假如我们要拿arr[0][2]元素:

*(pa[0]+2) = 3;
pa[0][2] = 3;//相同效果

行指针定义
类型名(*指针名)[数组的列]

int arr[3][4] = { {1,2,3,4},{1,2,3,4},{1,2,3,4} };
int (*pa)[3] = arr;

此时的pa就是指向列为3的二维数组的指针,还是拿到的第一行地址。

3.2.4.4 二维数组传参

二维数组传参,形式参数位置可以是二维数组也可以是指针形式。
先用一个代码感受一下

void test(int (*P)[5],int r,int c)
{
	for(int i = 0; i < r; i++)
	{
		for(int j = 0; j < c; j++)
		{
			printf("%d ",*(*(p+i)+j) );
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
	test(arr,3,5);
	return 0;
}

3.3 二级指针

二级指针听名字感觉有点流弊,二级!!高大上,但其实就是指向指针的指针而已。定义形式就是
指针类型** 指针名 = &被指向指针名。
注意此处要用两个*。

int a = 10;
int* pa = &a;
int** pa = &pa;

3.4 函数指针及使用

3.4.1 函数指针定义

函数指针顾名思义就是指向函数的指针,那应该如何定义呢?
指向函数返回类型 (*函数指针变量名)(函数参数列表)
例如:

//定义一个加法函数
int add(int x ,int y)
{
	return x+y;
}
//指向加法函数的指针
int (*padd) (int x, int y);

3.4.2 函数指针的使用

通过函数指针调用函数指针指向的函数。
用上面add函数举例。

int ret = add(1,2);
ret = padd(1,2);//相同效果

3.4.3 函数指针数组

我们可以使用函数指针数组来存储函数指针,之后就可以通过下标来调用不同函数。例如我们将加减乘除放进一个数组中。(前提是函数参数列表一样)。

int add(int x ,int y)
{
	return x+y;
}
int sub(int x ,int y)
{
	return x-y;
}
int mul(int x ,int y)
{
	return x*y;
}
int div(int x ,int y)
{
	return x/y;
}


int main()
{
	int(*p[5])(int x, int y) = {add,sub,mul,div};
	return 0;
}

3.5 void* 指针

void*指针就是无具体类型的指针。不能直接进行±整数和解引用的运算,但是它可以接收不同类型的指针。

void aaa(void* pf)
{
	//代码
}
int main()
{
	int a = 10;
	int* pa = &a;
	char ch ='a';
	char* pch = &ch;
	aaa(pa);
	aaa(ch);//两种使用都是正确的
	return 0;
}

3.6 const修饰指针

我们有时会需要将指针不能修改就要用const修饰。const修饰有两种情况:

  1. int const* pa = &a;此时const修饰的是*p,也就是指针指向的内容不能通过指针来改变了。但是指针自己可以改变。
  2. int *const pa = &a;此时const修饰的是p,也就是指针指向的内容能通过指针来改变了。但是指针自己不可以改变。
    其实我们如何轻松理解呢?const修饰的内容不能改变,当我们看见const修饰指针时先看const修饰的是什么(右边是什么),指针指向的内容(*p)还是指针(p)。

3.7 指针运算

3.7.1 指针±整数

在数组指针我们经常使用,看数组指针有介绍。

3.7.2 指针-指针

指针-指针得到两者之间差的元素个数(但是两个指针必须指向同一线性结构)

int main()
{
	int arr[10] = {0};
	int* p1 = &arr[5];
	int* p2 = &arr[2];
	printf("%d",p1-p2);//值为3
	return 0;		
}

3.7.3 指针关系运算

sizeof();
sizeof操作符求指针大小。

3.8 野指针

野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)

3.9 成因

1.指针未初始化

int *p;
*p = 20;

2.指针越界访问

int arr[10] = {0};
int* p = &arr[10];

3.指针指向空间被释放

int* test()
{
	int n = 100;
	return &n;
}
int main()
{
	int* p = test();
	printf("%d ",*p);
	return 0;
}

野指针危害是很大的,所以我们在写代码时要注意上述情况不要写出野指针。

四、思维导图

在这里插入图片描述

评论 151
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值