C语言进阶第二课-----------指针的进阶----------升级版

作者前言

✨✨✨✨✨✨
​ 作者介绍:

作者id:老秦包你会,
简单介绍:
喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨
个人主页::小小页面
gitee页面:秦大大

一个爱分享的小博主 欢迎小可爱们前来借鉴


指针进阶

  • **作者前言**
    • 前提复习
  • 字符指针
    • 指针数组
    • 数组指针
    • 数组传参和指针传参
    • 函数指针

前提复习

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针(指针变量)的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
  4. 指针的运算。(数组元素指针相减等于两者之间的元素个数)
  5. 内存单元是有编号的,编号 == 地址 ==指针

字符指针

用法一:

#include
int main()
{
	char a = 'w';
	char* p = &a;
	return 0;
}

这里的地址使用很简单,下面还有一种用法

#include
int main()
{
	const char* p = "abcdef";
	printf("%c", *p);
	printf("%s", p);
	return 0;
}

这里的p存储的是字符a的地址,我们可以想象成"abcdef"储存在一个数组中,传入p的值为数组名,也就是首元素的地址,就是a

还有一点需要注意的是"abcdef"是一个常量字符串,不能修改,但是会有些人通过*p来修改,这会造成程序崩溃,但是却不会报出警告,往往我们会加一个const 来把错误显示出来

指针数组

指针数组本质上就是一个数组,只是类型不一样而已
比如 字符数组–存放字符的数组
整形数组—存放整形的数组
指针数组—存放指针的数组,存放在数组中的元素都是指针类型
应用1:

#include
int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int arr1[] = { 2,3,4,5,6,7 };
	int arr2[] = { 3,4,5,6,7,8 };
	int arr3[] = { 4,5,6,7,8,9 };
	int arr4[] = { 5,6,7,8,9,10 };
	int* arr5[] = { arr,arr1,arr2,arr3,arr4 };//指针数组
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		int j = 0;
		for (j = 0; j < 6; j++)
		{
			printf("%d ", arr5[i][j]);
		}
		printf("\n");
	}
	return 0;
}

C语言进阶第二课-----------指针的进阶----------升级版_第1张图片

这种效果相当于写出了一个二维数组的效果

应用2:


```cpp
#include
int main()
{
	char* arr[5] = { "hello bit", "hehe", "penggeC","nitejiuyeke", "C++" };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

这里的效果就相当于写成了一个字符串组成的数组,原理就是数组arr元素保存的是字符串首字符地址,通过遍历取出来,然后一一打印出来

数组指针

指针数组是存放指针的数组,
数组指针是啥呢?,
我们可以想象一下,字符指针就是指向字符的指针, 整形指针就是指向整形的指针,所以,数组指针就是指向数组的指针

#include
int main()
{
	int a = 10;
	int* p = &a;

	char b = 'q';
	char* pr = &b;

	int arr[10];
	int* pa = arr;
	//数组名是首元素的地址
	//存在两个例外
	//sizeof (arr)  这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小
	// &arr 取出的是整个数组的地址


	return 0;
}

//数组名是首元素的地址
//存在两个例外
//sizeof (arr) 这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小
// &arr 取出的是整个数组的地址,如果从值的方向考虑就会发现arr和&arr的值是一样的
C语言进阶第二课-----------指针的进阶----------升级版_第2张图片
可以看出数组名和 &数组名的差别,指针类型决定了指针+1 加了几个字节

#include
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;//数组指针,里面的大小必须写 !!!!
	char arr1[100] = "123456";
	char(*pa)[100] = &arr1;//告诉我们pa是一个指针,指向的是char类型的数组地址
	char* arr2[5];
	char* (*pa2)[5] = &arr2;//告诉我们pa2是一个指针,指向的是char*类型的数组地址
	printf("%p\n", p);
	printf("%p\n", arr);
	return 0;
}

数组指针的写法就是 类型 (p)[数组大小,为了好理解我们可以理解为 char p[10],如果写出这样就会变成指针数组了,为了区分开来,才加()区分,指针类似还是char* 没有改变,只是写法不同而已

#include
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*pa)[10] = &arr;
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d", (*pa)[i]);//如果使用pa[i] 等价于*(pa + i)
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d", p[i]);
	}
	return 0;

输出的时候我们可以这样理解 arr[i] 可以访问元素, p = arr 所以p[i]可以访问元素
但是pa是数组指针 *pa可以消除& 得到arr , (*pa)[i]可以访问元素

一般我们使用数组指针是在二维数组使用的
在使用之前我们回忆一下一维数组传参给函数时

#include
void print(int* p, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%d ", p[i]); //p[i] == *(p + i)
	}

}
int main()
{
	int arr[5] = { 1,2,3,4,50 };
	print(arr, 5);


	return 0;
}

p[i] 等价于*(p + i), 所以打印地址一般用 arr + i 或者 &arr[i] 或者 &arr[0] + i

二维数组的应用

#include
void print(int arr[][3], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print1(int (*pa)[3], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", pa[i][j]);//pa[i] == *(pa + i)
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
	print(arr, 3, 3);
	print1(arr, 3, 3);

	return 0;
}

C语言进阶第二课-----------指针的进阶----------升级版_第3张图片
二维数组是一维数组的数组,我们可以想象一下二维数组arr里面的元素是一维数组,传参arr是首元素的地址,也就是把一维数组的整个地址传进去,
这里使用了数组指针pa ,如果要访问第二个元素可以写成 pa[1]

那我们就再扩展一下

int (*parr2)[10];
int (*parr3[10])[5];

第一行的意思就是,parr2是一个数组指针,指向的数组有10个元素,每个元素为整形
第二行代码的意思就是parr3是一个数组,进行存放数组指针的,可以存放10个数组指针,每个数组指针指向一个元素个数为5的数组,并且这个数组的元素类型为int

总结:这里我们要清楚数组指针的使用,及写法,比如 int (*pa)[10] = &arr
如果arr是一维数组,访问每个元素可以写成(pa)[i],不能写成pa[i],因为pa[i]
等价于
(pa + i) ,如果arr为二维数组,我们可以写成pa[i][j]访问每一个元素

数组传参和指针传参

一维数组传参
,数组传参本质上是,传递了首元素的地址数组传参,形参也可以是指针
C语言进阶第二课-----------指针的进阶----------升级版_第4张图片
数组传参,形参接收,形参的形式可以写成arr[] 或者 arr[大小] 或者 指针变量
如果碰见指针传参指针,我们就要思考要使用哪级指针接收,上图就是二级指针接收arr2数组首元素的地址,arr2数组首元素也是存储地址的

二维数传参
C语言进阶第二课-----------指针的进阶----------升级版_第5张图片
一级指针传参
C语言进阶第二课-----------指针的进阶----------升级版_第6张图片
二级指针传参
C语言进阶第二课-----------指针的进阶----------升级版_第7张图片
int* arr[5] int ** p = arr ,这个也可以
总结:指针传参有很多种传入方法,但是传入的意思是一样的

函数指针

数组指针是指向数组的指针,存放的是整个数组的地址 ----&数组名
那么函数指针是指向函数的指针,存放的是函数的地址,
那怎么得到函数的地址呢?

#include
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//&函数名就是函数的地址
	//函数名也是函数的地址
	//这两者没有区别
	printf("%p\n", &Add);
	printf("%p\n", Add);
	//函数指针
	int (*pA)(int, int) = Add;//意思就是pA为函数指针 ,指向的函数的类型为int 参数的类型也为int
	int (*pA1)(int, int) = &Add;
	printf("%p\n", pA);
	printf("%p\n", pA1);
	int tr = (*pA)(2, 3);// 解引用pA找到函数,并传入参数
	int re = pA(2, 3);
	printf("%d\n", tr);
	printf("%d\n", re);//这种写法是因为Add和&Add是一样的 ,我们调用函数是Add(2,3),所以pA存储Add,我们也可以使用pA(2,3)效果和调用函数是一样的
	return 0;
}

可以看出获取函数的地址有两种 &函数名或者函数名
我们调用函数除了使用函数名,还可以使用函数指针调用,使用函数指针调用,我们要熟悉函数调用的写法,Add(2,3),因为函数指针存储的是函数名 Add,所以通过函数指针调用函数,可以写成 pA (2,3),或者(*pA)(2,3)(可以理解为解引用找到函数并传参给函数)
C语言进阶第二课-----------指针的进阶----------升级版_第8张图片
从这个图中我们可以看出函数指针变量的类型

C语言进阶第二课-----------指针的进阶----------升级版_第9张图片
我们从这张图可以看出
代码1的意思就是 把0 强制类型转换成函数指针类型 ,并且解引用找到函数调用,这个函数没有参数
代码2 是一个函数声明,看到这里是不是很吃惊,那我们分析一下,
C语言进阶第二课-----------指针的进阶----------升级版_第10张图片
signal函数有两个参数,第一个参数的类型为int 第二个参数的类型是void()(int),因为不可能是函数调用,当我们吧函数去掉就会发现 void ()(int)是一个函数指针类型,如果我们换个类型就很好理解了 int signal( int, void(*)(int) ); 平时我们函数声明就是这样的,只是这个函数指针类型有写法有点特殊,但不改变这就是一个函数指针类型,

如果觉得这样写很麻烦,我们可以把这个函数指针类型重定义
C语言进阶第二课-----------指针的进阶----------升级版_第11张图片
typedef void(vi_int)(int);注意一下不要写成typedef void()(int) vi_int;这样是不行的

总结:这里我们要熟悉函数指针类型和指针传参 方式有很多但是意思是一样的

你可能感兴趣的:(c语言,开发语言)