【C语言】指针进阶

❤️欢迎来到我的博客❤️

字符指针

char*
一般使用:

int main()
{
	char ch = "a";
	char* pc = &ch;
	return 0;
}

这里的pc就是一个字符指针变量,这种方法是让我们的字符指针指向了一个字符变量。

另一种使用方式如下:
把字符串首字符的地址赋给了一个指针。

int main()
{
	const char* p = "Hello World";
	return 0;
}

因为这个字符串是一个常量字符串(不能被修改)所以在char*前面加一个const会更加严谨。

指针数组

指针数组就是存放指针的数组或存放地址的数组。

存放字符指针的数组:

int main()
{
	const char* arr[4] = { "abcd,qwer,asdf,zxcv" };
	return 0;
}

这四个字符串的首字符的地址都被放在了指针数组arr中。

打印:

int main()
{
	const char* arr[4] = { "abcd","qwer","asdf","zxcv" };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

运行效果:
在这里插入图片描述

存放整形指针的数组:

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int arr4[5] = { 4,5,6,7,8 };

	int* arr[4] = { arr1,arr2,arr3,arr4 };

	return 0;
}

打印:

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int arr4[5] = { 4,5,6,7,8 };

	int* arr[4] = { arr1,arr2,arr3,arr4 };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

运行效果:
在这里插入图片描述

数组指针

数组指针的定义

数组指针也是指针。
字符指针 - 存放字符地址的指针 - 指向字符的指针 char*
整形指针 - 存放整形地址的指针 - 指向整形的指针 int*
浮点型指针 - 指向浮点型的指针 float* double*
数组指针 - 存放数组地址的指针 - 指向数组的指针


数组指针的写法:

	int arr[10];
	int (*pa)[10] = &arr;

这里的“pa”就是一个数组指针
pa和 * 号结合,他是个指针,他指向了“[10]”(10个元素的数组),数组的每个元素的类型是" int "
&arr取出的是数组的地址,只有数组的地址才需要数组来接收

&数组名和数组名的区别

先来看一段代码:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", arr + 1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);

	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);
	return 0;
}

运行效果:
在这里插入图片描述
可以看到前面两种写法+1都只跳过了4个字节,证明他们的类型都是“ int* ",而最后一种写法跳过了40个字节(整个数组)证明他的类型是 int(*)[10],那么我们就能理解他们之间的区别了。

数组名 - 数组首元素的地址
&数组名 - 是数组的地址

数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样。
如果我们想拿到整个数组的地址我们就可以用 &数组名,如果只想拿到 数组首元素的地址 就可以用数组名
在一维数组中很少使用数组指针,在二维数组中才可能使用。

数组指针的使用

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

数组名表示首元素的地址,对于二维数组来说,他的第1行就是他的第一个元素,第一行的地址,相当于是一个一维数组的地址。
那么我们就可以放到一个数组指针里( int(*p)[4] )。

数组参数和指针参数

函数的参数应该如何设计呢?

一维数组传参

一维数组传参的时候形参该如何写?

void test(int arr[])
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

以上方法均可

二维数组传参

二维数组传参的时候形参该如何写?

void test(int arr[3][5])
{}
void test(int arr[][5])
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int(*arr)[5])
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}

以上方法均可

一级指针传参

指针变量传参,指针变量接收

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

void test(int* p)
{}
int main()
{
	int a = 10;
	int* p = &a;
	int arr[10];

	test(p);//可以传一级指针
	test(arr);//可以传一维数组的数组名
	test(&a);//可以传整型变量的地址
	return 0;
}

二级指针传参

void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

二级指针传参,形参部分用二级指针接收。

当函数的参数为二级指针的时候,可以接收什么参数?

void test(int** p)
{}
int main()
{
	int** ptr;
	int* pp;
	int* arr[10];

	test(ptr);//可以传二级指针变量
	test(&pp);//可以传一级指针变量的地址
	test(arr);//可以传指针数组的数组名

	return 0;
}

函数指针

前面我们讲到了数组指针:指向数组的指针。

	inr arr[10];
	int (*p) [10] = &arr;

那么函数指针是什么呢?其实就是指向函数的指针
其实函数也是有地址的,我们来看下面这条代码:

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	printf("%p\n", &Add);
	return 0;
}

运行效果:

在这里插入图片描述
可以看到,我们把Add函数的地址打印了出来。
那么函数指针应该怎么写呢?很简单,我们看下面这条代码:

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int,int) = &Add;
	//pf是一个存放函数地址的指针变量 — 函数指针
	return 0;
}

函数指针的写法和数组指针的写法非常相似。
首先pf是个指针,pf指向的是函数,函数的参数类型是(int int),pf指向的函数的返回类型是int,这样我们的pf就定义好了。

&函数名和函数名都是函数的地址
所以我们也可以这样写:

int (*pf)(int, int) = Add;

那我们能不能通过pf指针来调用Add函数呢?
可以。方法也很简单:

	int (*pf)(int, int) = Add;
	int ret = (*pf)(2, 3);//或int ret = pf(2,3);
	printf("%d\n", ret);

运行效果:
在这里插入图片描述

首先pf是个指针,指针解引用才能找到对应的函数(可以省略为pf,但方便理解我们还是写上:(*pf),调用的时候要传参,那我们就传个2和3:(*pf)(2,3),传过去之后函数计算完结果,那我们就把结果放在ret里面:int ret = (*pf)(2,3);

函数指针数组

想要写出函数指针数组,要从函数指针的基础上进行改造。

int (*pf)(int,int) = &Add;//函数指针
int (*pfA[5])(int,int) ={&Add};//函数指针数组

那么函数指针数组有什么作用呢?
我们来用函数指针数组写一个简易的计算器。

简易计算器

#include<stdio.h>
void menu()
{
	printf("************************************\n");
	printf("******* 1.add    2.sub       *******\n");
	printf("******* 3.mul    4.div       *******\n");
	printf("******* 0.exit               *******\n");
	printf("************************************\n");
}

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 (*pf[5])(int, int) = { 0,Add,Sub,Mul,Div };
//输入1对应Add,输入2对应Sub
//输入3对应Mul,输入4对应Div

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择->");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = pf[input](x, y);//通过输入数调用函数
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误,请重新选择\n");
		}
	} while (input);
	return 0;
}

这里的函数指针数组代替了switch函数,简化了很多代码,但是他也有缺点:因为函数指针数组限制了他(函数里的参数类型、返回类型必须一致),所以他只能计算双目操作数。

指向函数指针数组的指针

建议在函数指针数组的基础上进行修改。
我们先来看一下函数指针数组:

int (*pf[5])(int, int);

这里的pf和[]结合说明他是一个数组,但是我们希望他是指针,那我们就在这个基础上进行修改:

int (*pf[5])(int, int);//函数指针数组
int (*(*ppf)[5])(int, int) = &pf;//ppf - 指向函数指针数组的指针

我们只需要加上*然后再把ppf括起来,这时候ppf就是一个指针了。


以上就是本篇的全部内容了,希望大家看完能有所收获。

❤️ 创作不易,点个赞吧~❤️
  • 43
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值