当前位置: 首页 > news >正文

C语言——深入解析C语言指针:从基础到实践从入门到精通(四)


🏡润下:个人主页

  🔥个人专栏: 《C语言基础》

🏔️山高万仞,只登一步!

在这里插入图片描述
在这里插入图片描述

文章目录

  • 一.字符指针变量
  • 二.数组指针变量
    • 2.1 数组指针变量是什么
    • 2.2 数组指针变量如何初始化
  • 三.二维数组传参的本质
  • 四.函数指针变量
    • 4.1函数指针变量的创建
    • 4.2函数指针变量的使用
    • 4.3 typedef关键字
      • 4.3.1普通类型:
      • 4.3.2 指针类型
      • 4.3.3数组指针类型:
      • 4.3.4函数指针类型:
  • 五.函数指针数组
  • 六.转移表
  • 总结

前言:指针类型的不同会导致操作结果存在显著差异。本章将详细解析各类指针变量的特性差异,并通过具体实例展示其实际应用场景。

一.字符指针变量

字符型指针:char *

int main()
{char ch[] ="a";char* pc = &ch;printf("%c\n", *pc);char arr[] = { "abcdef" };char* pa = arr;printf("%s\n", pa);return 0;
}

1.字符或者字符串可以放在数组中,将数组地址传递给字符指针变量
当然字符或者字符串也可以不用创建数组的方式。
2.字符可以直接将变量地址给字符指针变量,字符串可以直接创建在字符指针变量中

int main()
{char ch = 'a';char* pc = &ch;printf("%c\n", *pc);char* pa = "abcdef";printf("%s\n", pa);return 0;
}

这两种创建方式的不同在于,通过数组的方式字符串赋值给字符指针变量,其中的字符串内容是可以修改的。如果直接将字符串赋值给字符指针,其中字符串内容无法修改的。是“ 常量字符串 ”


int main()
{char* pa = "abcdef";//常量字符串不可改*pa = 'abc';printf("%s\n", pa);return 0;
}

在这里插入图片描述
为了防止出现低级错误通常用const修饰

const char* pa = "abcdef";//常量字符串不可改

字符串abcdef是把首字符的地址放在字符指针变量中
在这里插入图片描述

一道有趣的代码: 😮

#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");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

运行结果:
在这里插入图片描述
❗️解析:
在这里插入图片描述
注:strcmp函数后期介绍

二.数组指针变量

2.1 数组指针变量是什么

之前学习指针数组,指针数组是一组存放指针(地址)的数组,是数组
那么数组指针本质上是指针变量
❗️类比不同类型的指针

字符指针:char *p 是指向字符的指针,存放的是字符的地址

char* p;
char ch;
p = &ch;

整型指针: int * p是指向整型的指针,存放的是整型的地址

int* p;
int a;
p = &a;

浮点型指针:float *p 是指向浮点型的指针,存放的是浮点型的地址

float *pb;
float b;
pb = &b;

那么

数组指针:int(*p) [ 数组大小] 是指向数组的指针,存放的是指向数组的地址
*和p先结合说明p是指针变量,指针指向了一个数组大小为10的整型数组,p是指针指向了一个数组,叫数组指针

数组指针变量指针数组
int (*p) [10]int * p[10]

数组指针变量中间的()不能去掉;去掉后p先和[ ]结合变成了 指针数组

2.2 数组指针变量如何初始化

通过取地址符&来取数组的地址

int arr[10] = { 0 };
int (*p)[10] = &arr;

在这里插入图片描述
p的类型和&arr的类型完全一样
注:数组指针一次跳过的字节数

int main()
{int arr[10] = { 0 };int (*p1)[10] = &arr;printf("&arr    =%p\n", *p1);printf("&arr+1  =%p\n", *(p1+1));return 0;
}  

在这里插入图片描述

三.二维数组传参的本质

之前在二维数组传参给一个函数的时候

#include<stdio.h>
void test(int arr[][5], int r, int c)
{int i = 0;int j = 0;for ( i = 0; i < r; i++){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;
}

形参写成的是二维数组的形式,还可以写成数组指针的形式
🔑 原因
二维数组可以看做是一维数组的数组,二维数组的每一个元素都是一个一维数组。二维数组的首元素第一行一维数组的地址
在这里插入图片描述
所以二维数组的数组名就表示为第一行的地址,即一维数组的地址。
二维数组本质上也是传递的地址,传递的是第一行一维数组的地址,那么把二维数组的形参就可以用数组指针来接收!
就可以改写为:


#include<stdio.h>
void test(int (*p) [5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (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;
}

在这里插入图片描述

🚩二维数组传参,形参可以写成数组,也可以写成指针的形式

四.函数指针变量

有前面的不同指针类型,可以类比函数指针变量是用来存放函数地址的,可以通过地址来调用函数

指针类型存放的地址类型指向的变量类型
字符指针字符的地址指向的是字符变量char *p
整形指针整形的地址指向的是整型变量int * p
数组指针数组的地址指向的是数组变量int (* p )[数组大小]
函数指针函数的地址指向的是函数变量type(*函数指针变量名)(参数…)

4.1函数指针变量的创建

数组有地址,函数也有地址

#include<stdio.h>
void test()
{printf("hehehe\n");
}
int main()
{printf("test   =%p\n", test);printf("&test  =%p\n", &test);return 0;
}

在这里插入图片描述

函数有地址,函数名就是函数的地址,也可以通过&函数名来获得地址
想把函数名(函数地址)存起来就要放在函数指针变量中

#include<stdio.h>
void test ()
{printf("hehe\n");
}
int Add(int a, int b)
{return a + b;
}
int main()
{int (*pa)(int, int) = &Add;int r = Add(10, 20);printf("%d\n", r);void (*pb)() = &test;test();return 0;
}

函数指针类型解析:

int*p)(int x,int y)|     |        ||     |      指向函数的参数类型和个数|     ||  函数指针变量名p指向函数的返回类型int*)(int x,int y)//该函数指针变量的类型

4.2函数指针变量的使用

在学习整形指针和字符指针的时候,可以通过 * 解引用找到元素值,在指针函数变量中也可以通过 *解引用函数名进行传参

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int main()
{int a = 10;int* pa = &a;printf("%d\n", *pa);int (*pf)(int, int) = &Add;int r = (*pf)(10, 20);printf("%d\n", r);return 0;
}

在之前我们都是通过调用函数都是直接通过函数名来进行传参调用

int r=Add(10,20);
printf("%d\n",r);

Add的地址能直接调用,那么(*pf)中存放的是函数Add的地址,也可以直接调用

int*pf)(int int=Add;
int r=pf(10,20);
printf("%d\n",r);

函数调用:
1.可以通过函数名直接调用
2.可以通过函数指针变量名(间接)调用。

在这里插入图片描述
实例

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int main()
{int (*pf)(int, int) = &Add;int w = (*pf)(10, 20);int r = pf(10, 20);printf("%d\n", w);printf("%d\n", r);return 0;
}

两段有趣的代码😆

1.*void*)())0)()

代码分析:
1.上述代码是一次函数的调用,这个函数没有参数 列如:(*pf)()(pf)是函数名
2.代码中void(*)()函数指针类型,这个指针指向的函数没有参数返回值为void,(void(*)())0表示把 0 强制转化为了该函数指针类型,相当于0地址处有这样一个返回值为void,没有参数的函数。
3.0的类型为函数指针变量,那么就可以解引用*(0)()
最终得到( *( void( *)())0)()


2.void*signal(intvoid(*)(int)))(int);

代码分析:
1.上述代码是一次函数声明。
2.函数名字叫:signal,函数有两个参数,intvoid(*)(int),第二个参数为函数指针类。signal函数的返回值也是void(*)(int)

函数声明,调用和定义:
以函数Add()为例

函数Add
函数调用Add()
函数定义int Add(int x,int y)
函数声明int Add(int,int)

对函数概念有疑惑的朋友可以看这篇文章:
🚀C语言——函数(超详细分析)

4.3 typedef关键字

typedef是用来类型重命名的,可以把复杂类型简单化
例如:

4.3.1普通类型:

感觉unsigned int书写起来不方便可以用typedef重命名uint

typedef unsigned int uint

#include<stdio.h>
typedef unsigned int uint;
int main()
{unsigned int num1= 100;uint num2 = 100;return 0;
}

在这里插入图片描述
num1和num2类型相同

4.3.2 指针类型

重命名:把int*重命为p_t

typedef int* p_t

typedef int* p_t;
int main()
{int* p1, p2;p_t p3, p4;return 0;
}

在这里插入图片描述

注:int *p1,p2为(int类型)

4.3.3数组指针类型:

把int(*)[5]重新命名为parr_t
typedef int(*parr_t)[5]


typedef int (*parr_t)[5];
int main()
{int arr[5] = { 0 };int (*p)[5] = &arr;parr_t p2 = &arr;return 0;
}

在这里插入图片描述

4.3.4函数指针类型:

typedef int(*pf_t)(int, int)

#include<stdio.h>
typedef int(*pf_t)(int, int);//新的函数名必须在*右边
int Add(int x, int y)
{return x + y;
}
int main()
{int (*pf)(int, int) = &Add;pf_t pf2 = Add(10,20);printf("%d\n", pf2);return 0;
}

在这里插入图片描述

简化代码:

void*signal(intvoid(*)(int)))(int

把void(*)(int)简化为pf_t

#include<stdio.h>
typedef void(*pf_t)(int);
void test()
{}
pf_t signal(int, pf_t);//函数声明
int main()
{pf_t signal(int, pf_t);signal(100, &test);//函数调用return 0;
}
pf_t signal(int n, pf_t p)//函数定义
{return p;
}

五.函数指针数组

有字符数组,整型数组,指针数组就可以类比出函数指针数组
函数地址存放至数组中就是函数指针数组

数组类型存放的数据类型格式[大小]
字符数组字符char [5]
整型数组整型int [5]
指针数组指针char *arr [5]
函数指针数组函数指针(地址)int(*parr[ 5 ])()

实例:

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int Sub(int a, int b)
{return a - b;
}
int Mul(int a, int b)
{return a * b;}
int Div(int a, int b)
{return a / b;
}
int main()
{int (*pArr[4])(int, int) = { Add,Sub,Mul,Div };int i = 0;for ( i = 0; i < 4; i++){int r = pArr[i](12, 4);printf("%d\n", r);}return 0;
}

在这里插入图片描述

六.转移表

函数数组指针的应用

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int Sub(int a, int b)
{return a - b;
}
int Mul(int a, int b)
{return a * b;}
int Div(int a, int b)
{return a / b;
}
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 x, y;int input = 1;int ret = 0;do{Menu();printf("请选择:\n");scanf("%d", &input);switch (input){case 1:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);case 4:printf("请输入操作数:");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;
}

优化:


#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int Sub(int a, int b)
{return a - b;
}
int Mul(int a, int b)
{return a * b;}
int Div(int a, int b)
{return a / b;
}
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 x, y;int input = 1;int ret = 0;int (*pArr[5])(int, int) = { 0,Add,Sub,Mul,Div };do{Menu();printf("请选择:\n");scanf("%d", &input);if ((input > 0) && (input <= 4)){printf("请输入操作数:");scanf("%d %d", &x, &y);ret = (*pArr[input])(x, y);printf("%d\n", ret);}else if (input == 0){printf("退出转移表\n");}else{printf("输入错误,请重新输入\n");}} while (input);return 0;
}

通过函数指针数组的优化把代码冗余的部分给省略了

总结

在这里插入图片描述

指针部分内容到此分享结束!感谢大家的支持!

指针所有分享!

🚀 C语言——深入解析C语言指针:从基础到实践从入门到精通(三)
🚀C语言——深入解析C语言指针:从基础到实践从入门到精通(二)
🚀C语言——深入解析C语言指针:从基础到实践从入门到精通(一)

http://www.dtcms.com/a/494424.html

相关文章:

  • Cursor 科技感的登录页面提示词
  • Ubuntu 24.04环境下的挂起转休眠
  • 【从0开始学习Java | 第21篇】网络编程综合练习
  • OpenCL初级教程
  • 【Spring AI】基于 sse 实现 MCP Server
  • vue使用限制仅允许上传 Excel 文件
  • dataease开发环境搭建
  • 一个网站开发周期wordpress导航条
  • 湖南城乡建设厅网站夜蝶直播app
  • list,咕咕咕!
  • iOS 混淆实战,多工具组合完成 IPA 混淆与加固(源码 + 成品 + 运维一体化方案)
  • Vue + Element UI 实现 el-scroll 滚动与鼠标滚轮监听全攻略
  • vue2版本的ruoyi-ui中使用vxe-table插件
  • 全国建设网站郑州seo优化公司
  • 用观察者模式通知UI刷新数据
  • 基于 AI 大模型的 UI 元素定位浏览器插件
  • Prism框架核心对象全解析
  • 阿里云服务器上部署Mosquitto
  • Android 设计模式实战手册(Kotlin 实战版)
  • Android thermal (5)_cooling device(下)
  • 活字格低代码平台实现移动端应用(安卓 /iOS)打包的技术方案与实践指南
  • SpringBoot电子商城系统
  • 解析 Qt Remote Objects:从框架原理到 Repcs 实践,以正点原子 RK3588 UI 系统为例
  • 【底层机制】【Android】Binder架构与原理
  • 揭阳市住房和城乡建设局网站网站类游戏网站开发
  • DeviceNet 转 MODBUS TCP罗克韦尔 ControlLogix PLC 与上位机在汽车零部件涂装生产线漆膜厚度精准控制的通讯配置案例
  • 【STM32项目开源】基于STM32的智能衣柜系统
  • python基于web的汽车班车车票管理系统/火车票预订系统/高铁预定系统 可在线选座
  • ssh终端管理多个k8s集群,快速切换配置
  • 景德镇建站公司全国建筑四库一平台