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

CSDN 2024-09-06 08:05:14 阅读 55

目录

什么是指针指针变量和操作符指针变量的定义操作符取地址操作符(&)解引用操作符(*)

指针分类及使用整型/浮点型/布尔类型指针/字符指针使用函数传参取得变量/变量修改

数组指针数组名与数组首元素定义数组指针与指针数组的区别数组指针的使用一维数组指针使用一维数组指针传参一维数组指针取变量/改变量

二维数组指针使用多维数组指针和行指针定义二维数组传参

二级指针函数指针及使用函数指针定义函数指针的使用函数指针数组

void* 指针const修饰指针指针运算指针+-整数指针-指针指针关系运算

野指针成因

在这里插入图片描述

什么是指针

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

指针变量和操作符

指针变量的定义

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

举个整形指针的例子

<code>int a = 10;

int* pf = &a;

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

操作符

取地址操作符(&)

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

解引用操作符(*)

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

使用形式就是 *指针变量名

int a = 10;

int* pf = &a;

*pf = 0;

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

指针分类及使用

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

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

//整型指针

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;//字符变量指针

使用

函数传参

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

void a1(int x, int y)

{

x++;

y++;

}

void a2(int* x, int* y)

{

(*x)++;

(*y)++;

}

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

取得变量/变量修改

对指针解引用就可以取得变量。

直接使用指针对变量进行修改。

int a = 10;

int* pf = &a;

printf("%d ",*pf);

printf("%d ",a);//两种是一个效果

*pf = 0;

数组指针

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

数组名与数组首元素

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

int arr[10] = { 0 };

int* pa1 = &arr[0];

int* pa2 = &arr;

int* pa3 = arr;

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

那c中就只有两种指向整个数组的情况

&数组名sizeof(数组名),sizeof操作符对数组求长度就是对整个数组求长度。

那pa3定义的指针是如何作用呢?其实pa3与pa1是完全相同的。数组名本身就是数组首元素地址。

定义

根据实际情况看需要的数组指针是需要指向整个数组还是数组元素,

数组类型* 指针名 = 数组名;

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

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

数组指针的使用

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

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

void print(int* arr,size_t len)//len表示数组长度

{

for(int i = 0; i < len; i++)

{

printf("%d ",arr[i]);

printf("%d ",*(arr+i) );

}

}

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

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

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

int arr[10] = { 0 };

int* pa = arr;

*(arr+9) = 1;

arr[9] = 1;//相同效果

二维数组指针使用
多维数组指针和行指针定义

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

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的二维数组的指针,还是拿到的第一行地址。

二维数组传参

二维数组传参,形式参数位置可以是二维数组也可以是指针形式。

先用一个代码感受一下

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;

}

二级指针

二级指针听名字感觉有点流弊,二级!!高大上,但其实就是指向指针的指针而已。定义形式就是

指针类型** 指针名 = &被指向指针名。

注意此处要用两个*。

int a = 10;

int* pa = &a;

int** pa = &pa;

函数指针及使用

函数指针定义

函数指针顾名思义就是指向函数的指针,那应该如何定义呢?

指向函数返回类型 (*函数指针变量名)(函数参数列表)

例如:

//定义一个加法函数

int add(int x ,int y)

{

return x+y;

}

//指向加法函数的指针

int (*padd) (int x, int y);

函数指针的使用

通过函数指针调用函数指针指向的函数。

用上面add函数举例。

int ret = add(1,2);

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

函数指针数组

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

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;

}

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;

}

const修饰指针

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

int const* pa = &a;此时const修饰的是*p,也就是指针指向的内容不能通过指针来改变了。但是指针自己可以改变。int *const pa = &a;此时const修饰的是p,也就是指针指向的内容能通过指针来改变了。但是指针自己不可以改变。

其实我们如何轻松理解呢?const修饰的内容不能改变,当我们看见const修饰指针时先看const修饰的是什么(右边是什么),指针指向的内容(*p)还是指针(p)。

指针运算

指针±整数

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

指针-指针

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

int main()

{

int arr[10] = { 0};

int* p1 = &arr[5];

int* p2 = &arr[2];

printf("%d",p1-p2);//值为3

return 0;

}

指针关系运算

sizeof();

sizeof操作符求指针大小。

野指针

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

成因

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;

}

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

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。