#include<初见C语言之指针(3)>

本文详细介绍了C语言中字符指针、数组指针、函数指针及其应用,包括数组指针的初始化、二维数组传参的本质、函数指针的使用以及函数指针数组的运用,还展示了如何使用转移表简化计算器程序。
摘要由CSDN通过智能技术生成

目录

一、字符指针变量

二、数组指针变量

1.什么是数组指针变量?

2.数组指针怎么初始化?

三、二组数组传参的本质

四、函数指针变量

1.什么是函数指针变量?

2.函数指针变量使用 

 3.有趣代码

 3.1typedef关键字

五、函数指针数组

六、转移表

总结


一、字符指针变量

字符指针变量的指针类型是char*;

一般使用:

int main()
{
    char ch = 'w';
    char* pc = &ch;
    printf("%c\n", ch);
    *pc = 'p';
    printf("%c\n", *pc);
    
    return 0;

}

%c是打印字符的,%s是打印字符串的首地址

其他用法:

int main()
{
    const char* p= "hello world";//常量字符串,是不能修改的
    printf("%c\n", *p);
    //*p = 'q';//err
    
    return 0;

}

很容易把代码hello world放在指针*p中,但是本质是把字符串的首地址放在*p中。

画图演示:

下面我们再来看看一段代码:

 #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;
}

画图演示:

所以str1和str2不相同,str3和str4相同;(常量字符串,是不能修改的)


二、数组指针变量

1.什么是数组指针变量?

类比的方法:

  • 字符指针 char* p 指向字符的指针,存放的是字符地址
  • 整型指针 char* p 指向整型的指针,存放的是整型地址

所以数组指针是一种指针变量,是存放数组地址的指针变量

char ch = 'w';

char* pc =&ch;//字符指针

int n =10;

int* p =&n;//整型指针

数组指针变量

int (*p)[10];

解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组

 注意:[ ]的优先级要高于*,所以必须先加()保证p和*结合

2.数组指针怎么初始化?

在前面的学习中我们就知道,数组名是数组首元素的地址

int main()
{
    int arr[10] = { 10 };
    int(*p)[10] = &arr;//取出的是数组的地址
    //p应该是数组指针
    
    return 0;
}

&arr和p的类型是一致的 


三、二组数组传参的本质

二维数组的一般写法

void test(int arr[3][5],int r,int c)
{
    int i = 0;
    for (i = 0; i < r; i++)
    {
        int j = 0;
        for (j = 0; j < c; j++)
        {
            printf("%d ", arr[i][j]);
        }
    }
}


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;
}

二维数组名是谁的地址?

根据数组名是数组⾸元素的地址这个规则

  1. 二维数组的首元素就是第一行(一维数组)
  2. 每一行都是一个元素(一维数组)

画图演示:

void test(int (*p)[5], 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));// arr[i]==*(arr+i)
        }
        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;
}

 结论:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。


四、函数指针变量

1.什么是函数指针变量?

类比:

  • 数组指针,指向数组,存放数组地址
  • 整型指针,指向整型,存放整型地址

函数指针,指向函数,存放函数的地址

int Add(int a, int b)
{
    return a + b;
}

int* test(char* s)
{
    return 0;
}
//
函数指针变量的写法和数组指针变量写法类似

int main()
{
    int*(*pt)(char) = &test;
    int arr[8] = { 0 };
    int(*pa)[8] = &arr;//pa是数组指针变量
    int (*pf)(int,int) = &Add;//pf是函数指针变量
    int x = 10;
    int y = 20;
    int z = Add(x,y);
    printf("%p\n", &Add);
    printf("%p\n", Add);


    return 0;
}

&函数名和函数名都是表示函数地址 

函数指针变量

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

解析: 

2.函数指针变量使用 

int Add(int a, int b)
{
	return a + b;
}

int main()
{
	int (*pf1)(int, int) = &Add;//pf就是函数指针变量
	int (*pf2)(int, int) = Add;

	int r1 = (*pf1)(3,7);
	int r2 = (*pf2)(3,7);//*可以
	int r4 = pf2(3,7);
	int r3 = Add(3, 7);

	printf("%d\n", r1);
	printf("%d\n", r2);
	printf("%d\n", r3);
	printf("%d\n", r4);

	return 0;
}

 3.有趣代码

代码1:

void (*p)();

int main()
{
    (*(void (*)())0)();//函数调用
    //1.将0强制类型转换成void(*)()类型的函数指针
    //2.调用0地址放的这个函数
    
    return 0;
}

 代码2

int main()
{
    void (*signal(int, void(*)(int)))(int);//函数声明
    //声明的函数名叫:signal
    //signal的函数有2个参数,第一个参数的类型是int
    //第二个参数的类型是void(*)(int)的函数指针类型,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
    // signal函数的返回类型是void(*)(int)的函数指针,该指针可以指向一个函数,指向的函数参数是int,返回类型是void
    
    //void (*)(int)signal(int, void(*)(int));//err
    return 0;
}

 3.1typedef关键字

typedef对整型类型
typedef unsigned int uint;

int main()
{
    unsigned int num1;
    uint num2;
    return 0;
}


typedef对指针类型重命名
typedef int* pint;

int main()
{
    int* p1 = NULL;
    pint p2 = NULL;
    return 0;
}


数组指针重命名
typedef int(*parr_t)[5];
//*parr_t等价于int(*)[5]
int main()
{
    int arr[5] = { 10 };
    int(*p)[5] = &arr;//p是数组指针变量,p是变量的名字
    //int (*)[5] -- 数组指针类型
    parr_t p2 = &arr;
}
void test(char* s)
{

}


//对函数指针类型重命名产生新的类型pf_t
typedef void(*pf_t)(char*);

int main()
{
    void(*pf)(char*) = test;
    //void (*)(char*)
    pf_t pf2 = test;
}


五、函数指针数组

类比:

  • 字符指针数组 char* arr1[5];
  • 整型指针数组  int* arr2[5];

如果要把多个相同类型的函数指针存放在一个数组中,这个数组就叫:函数指针数组

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 (*pf1)(int,int ) = add;
    int (*pf2)(int,int ) = sub;
    int (*pf3)(int,int ) = mul;
    int (*pf4)(int,int ) = div;


    int (*pfarr[4])(int, int) = { add,sub,mul,div };//pfarr就是函数指针数组
    int i = 0;
    for(i=0;i<4;i++)
    {
        int ret = pfarr[i](8,4);
        printf("%d\n", ret);
    }

    return 0;
}

int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };

解析:

 

六、转移表

我们这里写一个计算机,实现加减乘除法;

第一种写法:

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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		
		menu();
		int input = 0;
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d", ret);
			break;
		case 3:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d", ret);
			break;
		case 4:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

我们可以看到选择这一部分是很冗余的,所以我们可以利用函数指针数组来解决。

第二种写法:

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;
}
//想写一个计算器
//完成2个整数的运行
//1.加法
//2.减法
//3.乘法
//4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	//创建一个函数指针的数组
	//转移表
	int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
	
	do
	{
		menu();
		int input = 0;
		printf("请选择:");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			pfArr[input](x, y);
		}
		else if (input == 0)
		{
			printf("退出计算机\n");
		}
		else
		{
			printf("选择错误\n");

		}

	} while (input);
	

	return 0;
}

这一种写法就看起来清爽很多,这样也可以添加更多的功能。

我们这里可以小结一下之前所学指针:

拓展:

char*(*(*p)[4])(int,char*) = &pfArr;//取出的是函数指针数组的地址

//p就是指向函数指针数组的指针;

所以我们就有第三种写法:

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;
}
想写一个计算器
完成2个整数的运行
1.加法
2.减法
3.乘法
4.除法
void menu()
{
	printf("**************************\n");
	printf("*****1.Add     2.Sub******\n");
	printf("*****3.Mul     4.Div******\n");
	printf("*****     0.exit    ******\n");
	printf("**************************\n");
	printf("**************************\n");
	printf("**************************\n");
}
void calu(int (*pf)(int,int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入2个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d", ret);
}
int main()
{
	int input = 0;
	
	do
	{
		menu();
		int input = 0;
		printf("请选择:");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			calu(Add);
			break;
		case 2:
			calu(Sub);
			break;
		case 3:
			calu(Mul);
			break;
		case 4:
			calu(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}

	} while (input);

	return 0;
}

第二种是将函数指针数作为跳板、第三种写法我们都是将 取出函数指针数组的地址作为跳板


总结

指针的第三节主要是字符指针变量、数组指针变量、二维数组传参的本质、函数指针变量、函数指针数组以及转移表。

  • 42
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值