详细讲解C语言15(C语言系列)

目录

前言:

初阶指针的概念回顾:

字符指针:

指针数组:

数组指针: 

数组指针的定义:

&数组名vs数组名:

数组指针应该怎么用? 

一维数组: 

二维数组: 

数组参数、指针参数:

一维数组传参:

 二维数组传参:

一级指针传参: 

二级指针传参: 

函数指针: 

函数指针数组:

指向函数指针数组的指针:

回调函数:

结束语:


前言:

之前我和大家分享过初识指针的一些知识,如果没有看过的小伙伴请点击这里去康康吧(http://t.csdn.cn/kGepB),那接下来我会继续深入指针与大家继续分享指针相关的一些知识,大家快来一起学习吧!

初阶指针的概念回顾:

1.指针就是一个变量,用来存放地址,地址唯一标识一块内存空间。

2.指针的大小是固定的4/8个字节(32位平台/64位平台)。

3.指针是有类型的,指针的类型决定了指针的+ - 整数的步长,以及指针解引用时的权限大小。

4.指针的运算。

字符指针:

(char*):存放字符的指针。

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char ch = 'W';
	char* pc = &ch;//pc为字符指针
	char* ps = "abcdef";//ps为常量字符串指针
	//*ps = 'w';
	printf("%c\n", *ps);
	return 0;
}


代码运行结果:

修改值的时候常量字符串是不能被修改的。 

如下所示: 

下面我们来看一下以下这个代码:
代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char str1[] = "hello bit";
	char str2[] = "hello bit";
	const char* str3 = "hello bit";
	const char* str4 = "hello bit";
	if (str1 == str2)
	{
		printf("str1 and str2 are same\n");
	}
	else
	{
		printf("str1 and str2 are not same\n");
	}
	if (str3 == str4)
	{
		printf("str3 and str4 are same\n");
	}
	else
	{
		printf("str3 and str4 are not same\n");
	}
	return 0;
}

代码运行结果:

解析:

两个字符串用等号(==)来比较的时候比较的是两个字符串的首地址,要比较字符串内容的时候则要采用strcmp来进行比较。

指针数组:

指针数组是一个存放指针的数组。

int* arr[10]整形指针的数组
char* arr2[10]一级字符指针的数组
char** arr3[4]二级指针数组的数据

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
//指针数组---存放指针的数组
#include<stdio.h>
int main()
{
	char* arr[5] = { "abcdef","zhangsan","wnagwu","lisi","ruhua" };//存放的是数组的首地址
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s ", arr[i]);
		printf("\n");
	}
	return 0;
}


代码运行截图:

解析:
 

 

练习:用一维数组模仿实现一个二维数组。

代码展示:
 

#define _CRT_SECURE_NO_WARNINGS 1
//用一维数组模仿实现一个二维数组
#include<stdio.h>
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* arr[] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

代码运行结果:

解析:

数组指针: 

数组指针的定义:

数组指针是指针,下面我们来回顾一下之前学习过的指针都有哪些。

int* pt整形指针指向整形的指针存放整形变量的地址
float* pf浮点型指针指向浮点型的指针存放浮点型变量的地址
char* pc字符型指针指向字符型的指针存放字符型变量的地址
int (*p)[5]数组指针指向数组的指针存放数组变量的地址

int arr[5] = {1,2,3,4,5};

int (*pt)[5] = &arr;数组指针:取出的是数组的地址存放到pa中,pa是数组指针变量。

其中 int (*) [5] 是数组指针的类型。

注意:上述中的数组长度【5必须要保持一致。

&数组名vs数组名:

这里我们来回顾一下之前学习有关数组名的知识:
1.数组名是数组首元素的地址。

2.&数组名:代表的是取出整个数组的地址。

3.sizeof(数组名):计算出的是整个数组的大小。

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	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;
}


代码运行结果:


解析:

 

数组指针应该怎么用? 

一维数组: 

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
//一维数组
#include<stdio.h>
//采用数组来接收
void print1(int arr[], int s)
{
	int i = 0;
	for (i = 0; i < s; i++)
	{
		printf("%d ", arr[i]);
	}
}
//采用指针来接收
void print2(int* pa, int s)
{
	int i = 0;
	for (i = 0; i < s; i++)
	{
		printf("%d ", *(pa + i));
	}
}
//采用数组指针来接收
void print3(int (*pa)[10], int s)
{
	int i = 0;
	for (i = 0; i < s; i++)
	{
		printf("%d ", (*pa)[i]);
	}
}
void test1()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print1(arr, sz);
	printf("\n");
	print2(arr, sz);
	printf("\n");
	print3(&arr, sz);//需要用数组指针来接收
}
int main()
{
	test1();
	return 0;
}


代码运行截图:

 

二维数组: 

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
//二维数组
#include<stdio.h>
//采用数组接收
void print1(int arr[3][5], int n, int m)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
//用数组指针来接收
void print2(int (*pa)[5], int n, int m)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		int j = 0;
		for (j = 0; j < m; j++)
		{
			printf("%d ", *(*(pa + i) + j));
		}
		printf("\n");
	}
}
void test2()
{
	int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };
	print1(arr, 3, 5);
	printf("\n");
	print2(arr, 3, 5);
}
int main()
{
	test2();
	return 0;
}


代码运行结果:

下面我们来梳理一下数组指针和指针数组吧!

int arr[5]整型数组
int* paar[5]指针数组
int (*paar2)[5]数组指针
int (* paar3[10])[5]数组指针类型

 

 

数组参数、指针参数:

数组传参可以是数组也可以是指针。 

一维数组传参:

 二维数组传参:

一级指针传参: 

一级指针传参用一级指针接收即可。

代码展示:

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


运行结果截图:

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

二级指针传参: 

代码展示:
 

#define _CRT_SECURE_NO_WARNINGS 1
//二级指针传参
#include<stdio.h>
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 (*)()0))():

 

void (*signal(int,void(*)(int)))(int):

 可以对上述声明进行类型重定义:

 这样看会起来更加便于阅读

 注意:重定义的时候不可以写为:typedef void(*)(int) pf_t;这样写是错误的写法。

 

函数指针数组:

函数指针的写法为:int(*pt)( int, int )

则函数指针数组的写法为:int (*ptArr[2])( int , int )

int (*ptArr[2])( int , int ) = {Add,Sub};

可以存放多个参数相同,返回类型相同的函数的地址。

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int main()
{
	int (*ptArr[2])(int, int) = { Add,Sub };
	int ret = ptArr[0](2, 3);
	printf("%d\n", ret);
	ret = ptArr[1](2, 3);
	printf("%d\n", ret);
	return 0;
}


代码运行结果:

题目:写一个计算器:

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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;
}
void menu()
{
	printf("*****************************\n");
	printf("******* 1.Add  2.Sub  *******\n");
	printf("******* 3.Mul  4.Div  *******\n");
	printf("******* 0.exit        *******\n");
	printf("*****************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			printf("请输入两个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择:>\n");
			break;
		}
	} while (input);
	return 0;
}


代码运行结果:

采用函数指针数组来简化代码。

简化代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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;
}
void menu()
{
	printf("*****************************\n");
	printf("******* 1.Add  2.Sub  *******\n");
	printf("******* 3.Mul  4.Div  *******\n");
	printf("******* 0.exit        *******\n");
	printf("*****************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//函数指针数组 ---- 转移表
		int (*ptArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
		if (input == 0)
		{
			printf("退出游戏\n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = ptArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}


代码运行结果:
 

 采用函数指针数组的优点可以大大减少代码的冗余。

指向函数指针数组的指针:

函数指针:

int (*pf)(int, int )

函数指针数组:

int (*ptArr[3])(int, int)

指向函数指针数组的指针:

int ( * (*ptr)[4] ) (int, int) = &ptArr;

回调函数:

概念:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对事件或条件进行响应。

题目:用回调函数来写计算器:

代码展示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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;
}
void menu()
{
	printf("*****************************\n");
	printf("******* 1.Add  2.Sub  *******\n");
	printf("******* 3.Mul  4.Div  *******\n");
	printf("******* 0.exit        *******\n");
	printf("*****************************\n");
}
void calc(int (*p)(int,int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>\n");
	scanf("%d %d", &x, &y);
	ret = p(x, y);
	printf("%d\n", ret);
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}


运行结果截图:

结束语:

这次小编和大家分享了指针进阶的相关内容,希望对大家有所帮助,下次小编将会给大家分享指针的一些具体练习,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力敲代码的小白✧٩(ˊωˋ*)و✧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值