【C语言】进阶——指针

 

目录

①(●'◡'●)前言

1.字符指针 

✌字符指针和数组笔试题 

2.指针数组 和数组指针

👊指针数组 

👊数组指针 

 👊&数组名和数组名

3.数组传参和指针传参 

👊一维数组传参

👊二维数组传参

 👊一级指针传参

👊二级指针传参

4.函数指针 

5.函数指针数组

👊函数指针数组应用 

6.函数指针数组的指针

7.回调函数 

👊qsort() 

冒泡排序通用版


 

①(●'◡'●)前言

在之前【C语言】入门——指针介绍了指针的概念

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

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

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4. 指针的运算。

这一篇介绍更深度的指针内容 

1.字符指针 

步长最短的字符型指针,字符指针就是用来存放字符变量(或字符串变量)的指针

当存储字符串变量时,会像数组一样只存入首字母的地址,在解引用时可以根据首地址依次往后访问并解引用,直到遇到结束标志 '\0'

由此看来指针貌似和数组有点相似。

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

 

✌字符指针和数组笔试题 

//字符指针笔试题
int main()
{char arr1[] = { "Hello World" };char arr2[] = { "Hello World" };const char* str1 = "Hello World";const char* str2 = "Hello World";if (arr1 == arr2)printf("arr1 and arr2 are same\n");elseprintf("arr1 and arr2 are not same\n");if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");return 0;
}

arr1和arr2是两个独立的数组,自然地址不一样,独立空间,所以不相等;

str1和str2是因为指向同一块空间,因为两个相同的常量在内存中只会开辟一块空间; 

 

 

2.指针数组 和数组指针

指针数组是数组,数组内存放的是指针;

数组指针是指针,存放数组的地址。

数组指针与指针数组容易混淆

int arr[5];

arr是一个数组,每个元素是int类型的 ,有5个元素

int* parr1[10];

parr1是一个数组,数组10个元素,每个元素的类型是int*

int (*parr2)[10];

parr2是一个指向数组的指针,指向的数组有10个元素,每个元素的类型是int

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

parr3是一个数组,数组有10个元素,每个元素的类型是:int(*)[5]

parr3是存放数组指针的数组 

👊指针数组 

//指针数组
int main()
{int a = 1, b = 2, c = 3;int* pa = &a;int* pb = &b;int* pc = &c;int* arr[3] = { pa,pb,pc };int i = 0;for (i = 0; i < 3; i++){printf("地址:%p\n", arr[i]);printf("值为:%d\n", *arr[i]);}return 0;
}

 

 定义三个变量,取各自他们的地址赋给不同的指针,指针为int*,再将指针放到数组内存放,

&arr的类型为:int* (*arr)[3];

arr的类型为int* arr;

👊数组指针 

//数组指针
#include <stdio.h>
int main()
{int arr[10] = { 0 };int (*p)[10] = &arr;//数组指针,存放整形数组arr的地址return 0;
} 

 👊&数组名和数组名

//&数组名与数组名
int main()
{int arr[5] = { 1,2,3,4,5 };int(*pa)[5] = &arr;printf("这是起始地址:%p %p\n", arr, pa);printf("这是+1后的地址:%p %p\n", arr + 1, pa + 1);return 0;
}

 

&arr+1,加一个步长,但是是整个数组的地址+1,所以是跳过整个数组大小;

数组5个元素,0x00fbfe58是首元素地址,58-5c-60-64-68-6c;

跳过了整个数组大小20个字节;

arr+1; 首元素地址+1,是跳过一个元素大小; 

58-5c;整型数组,一个整型4个字节,跳过一个元素即是4个字节;

3.数组传参和指针传参 

👊一维数组传参

//一维数组传参
void test1(int arr[])
{}//一维数组可以省略元素数,也可以写上
void test1(int*pa)
{}//用一级指针接收一维数组
void test2(int*arr2[10])
{}//形参用指针数组接收指针数组传参
void test2(int**ppa)
{}//指针数组本质上是二级指针
int main()
{int arr1[10] = { 0 };int* arr2[10] = { 0 };test1(arr1);test2(arr2);return 0;
}

👊二维数组传参

三种方式:

完整传参,行和列都不省略;

省略行;

数组指针接受

//二维数组传参
void test(int arr[3][5])
{}//完整接收
void test(int arr[][5])
{}//省略行,是可以的
void test(int(*pa)[5])
{}//用我们前面的数组指针接收
void test(int** pa)
{}//是错误的
int main()
{int arr[3][3] = { 0 };test(arr);return 0;
}

 👊一级指针传参

//一级指针传参
void test1(int* pa, int sz)
{}//传数组名,用指针接收
void test2(int* pa, int sz)
{}//传指针,用指针接收
int main()
{int arr[3] = { 1,2,3 };int sz = sizeof(arr) / sizeof(arr[0]);int* pa = arr;test1(arr, sz);test2(pa, sz);return 0;
}

👊二级指针传参

//二级指针传参
void test1(int**pa)
{}//接收的是二级指针
void test2(int**pa)
{}//接收的一级指针的地址
int main()
{int a = 10;int* pa = &a;//一级指针int** ppa = &pa;//二级指针test1(ppa);//传二级指针test2(&pa);//将一级指针的地址取出来return 0;
}

4.函数指针 

函数指针是指向函数的指针;函数名就是地址

函数指针由三部分组成:

类型、指针、形参,

类型和形参可以为空,当想要调用函数时,只需要通过指针,并传入参数,就能正常使用函数。 

int Add(int x,int y)
{return x + y;
}
int main()
{int (*p)(int, int) = &Add;printf("%d", p(2, 3));		//5return 0;
}

 来个函数指针趣题

void (*signal(int, void(*)(int)))(int);

1.函数指针: void(*)(int) 

2.函数名: signal

3.signal函数的参数: int,  void(*)(int);   一个整型,一个函数指针

4.返回值:signal函数的返回值是指针 

5.函数指针数组

 将一些函数地址存入数组中,就得到了函数指针数组。

//函数指针数组
int Add(int x, int y)
{return x + y;
}
int Sub(const int x, const int y)
{return x - y;
}
int main()
{int(*pfun[2])(const int x, const int y) = { Add,Sub };printf("%d\n", pfun[0](2, 3));	//5printf("%d\n", pfun[1](5, 3));	//2return 0;
}

函数名就是地址名,不需要&地址,函数形参不能省略 

👊函数指针数组应用 

//简易整型计算器
#include<stdio.h>
void menu()
{printf("*****计算器*****\n");printf("**1.Add  2.Sub**\n");printf("**3.Mul  4.DIV**\n");printf("*****0.exit*****\n");}
int add(const int x, const int y)
{return x + y;
}
int sub(const int x, const int y)
{return x - y;
}
int mul(const int x, const int y)
{return x * y;
}
int div(const int x, const int y)
{return x / y;
}
int main()
{int input = 1;int(*calc[5])(const int x, const int y) = { 0,add,sub,mul,div };//放0的原因是和菜单中的序号对应上while (input){menu();printf("请输入:>");scanf("%d", &input);if (input > 0 && input < 5){int x = 0, y = 0;printf("请输入两个数:");scanf("%d %d", &x, &y);printf("计算结果为%d\n", calc[input](x, y));}else if (input >= 5)printf("选择错误,请重新选择!\n");}printf("退出计算器\n");return 0;
}

 

 

6.函数指针数组的指针

 套娃式,本质是指针;

void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函数指针pfunvoid (*pfun)(const char*) = test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;
}

 例子2:

//函数指针数组的指针
int add(int x, int y)
{return x + y;
}
int main()
{//这是函数指针数组int (*pa[5])(int x, int y) = { add };//这是函数指针数组的指针,需要取出地址int(*(*ppa)[5])(int x, int y) = &pa;printf("这是函数指针数组的指针%p\n", ppa);printf("这是&函数指针数组后的地址%p\n", &pa);return 0;
}

7.回调函数 

回调函数就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,这就是回调函数。

(依赖函数指针,有了函数指针,才能实现回调函数) 

👊qsort() 

qsort():快速排序

库函数,头文件#include<stdlib.h>

qsort函数可以进行各种数据的排序

 

void qsort(void* base,                 //待排序数组的第一个元素的地址

size_t num,                                 //待排序数组的元素个数

size_t size,                                //待排序数组中一个元素的大小

int (* cmp)(const void* e1, const void* e2)        //函数指针,自己定义排序函数

整型实例 

//qsort()
#include<stdio.h>
#include<stdlib.h>
int cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
int main()
{int arr[] = {5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的

e1和e2中存放的是需要比较的两个元素的地址

void* 不能直接进行计算,需要强转成其他类型;

结构体示例 

#include<string.h>
#include<stdlib.h>
struct Stu
{char name[20];int age;
};int cmp_by_name(const void* e1,const void* e2)
{return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}
int main()
{struct Stu arr[] = { {"zhangsan",20},{"wangwu",15} };//元素个数int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_by_name);return 0;
}

strcmp()是根据字符字典顺序比较的,后面的大,需要包含头文件#include<stdlib.h> 

qsort函数中就用到了回调函数的知识,使用qsort,它都会去调用比较函数。 

冒泡排序通用版

用冒泡排序的思路去模仿qsort(); 

#include<string.h>
struct Stu
{char name[20];int age;
};
//结构体年龄比较
int cmp_age(void* e1, void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//字节交换,size是一个数据的字节大小
void swap(char* buf1, char* buf2, size_t size)
{int k = 0;for (k = 0; k < size; k++){char temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;}
}
//整型比较
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//打印
void print(int* arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
//冒泡模拟qsort
void my_qsort(void* base, size_t num, size_t size, int(*cmp_name)(const void* p1, const void* p2))
{int i = 0;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if (cmp_name((char*)base + j * size, (char*)base + (j + 1) * size) > 0)		//得到的返回值,如果大于0,就交换{swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int main()
{struct Stu arr[] = { {"zhangsan",15},{"lisi",12},{"wangwu",30} };int iarr[] = { 9,8,7,6,5,4,3,2,1,0 };//整型数组的元素个数int isz = sizeof(iarr) / sizeof(iarr[0]);my_qsort(iarr, isz, sizeof(iarr[0]), cmp_int);print(iarr, isz);//结构体的元素个数int sz = sizeof(arr) / sizeof(arr[0]);my_qsort(arr, sz, sizeof(arr[0]), cmp_age);return 0;
}

感谢你看到这里

以上就是我对进阶指针的介绍,身为初学者,自知有很多不足和需要改善的地方,希望大佬们指点一二,感激不急!!!

⭐愿星光照亮每一位赶路人 ⭐

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云原生之使用Docker部署Nas-Cab个人NAS平台

云原生之使用Docker部署Nas-Cab个人NAS平台 一、Nas-Cab介绍二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Nas-Cab镜像五、部署Nas-Cab5.1 创建挂载目录5.2 创建Nas-Cab容…

利用idea新创建maven项目时的一些基本配置

1.修改项目默认的maven仓库 file->Settings->Build 2.设置项目的jdk版本 设置完点OK即可。 同样的我们还需要在项目配置中进行修改。 通过以上设置一般就可以解决jdk版本不兼容地方问题。

稀土系储氢合金 压力-组成等温线 PCI 的测试方法

声明 本文是学习GB-T 29918-2023 稀土系储氢合金 压力-组成等温线 PCI 的测试方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 5 方法2:电化学法 5.1 方法提要 以储氢合金作负极&#xff0c;烧结氢氧化亚镍作正极&#xff0c;氢氧化钾水溶液作电…

postgresql-视图

postgresql-视图 视图概述使用视图的好处 创建视图修改视图删除视图递归视图可更新视图WITH CHECK OPTION 视图概述 视图&#xff08;View&#xff09;本质上是一个存储在数据库中的查询语句。视图本身不包含数据&#xff0c;也被称为 虚拟表。我们在创建视图时给它指定了一个…

数字IC设计之时序分析基础概念汇总

1 时钟Clock 理想的时钟模型是一个占空比为50%且周期固定的方波。时钟是FPGA中同步电路逻辑运行的一个基准。理想的时钟信号如下图: 2 时钟抖动Clock Jitter 理想的时钟信号是完美的方波&#xff0c;但是实际的方波是存在一些时钟抖动的。那么什么是时钟抖动呢?时钟抖动&#…

苹果电脑Mac系统运行速度又卡又慢是怎么回事?

通常大家处理Mac运行速度慢的方法不是重启就是清空废纸篓&#xff0c;但是这两种方法对于Mac提速性能的效果是微之甚微的&#xff0c;想要彻底解决Mac运行速度慢&#xff0c;你应该试试一下三种方法~ 1、清理磁盘空间 硬盘空间过少是Mac运行变慢很大的一个因素&#xff0c;各…

【LeetCode刷题笔记】动态规划 — 70.爬楼梯

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

Unity中 UI Shader的基本功能

文章目录 前言一、实现思路1、暴露一个 2D 类型的属性来接受UI的纹理2、设置shader的层级为TransParent半透明渲染层级&#xff0c;一般UI都是在这个渲染层级3、更改混合模式&#xff0c;是 UI 使用的纹理&#xff0c;该透明的地方透明 二、代码实现 前言 Unity中 UI Shader的…

Python爬虫逆向猿人学刷题系列——第七题

题目&#xff1a;采集这5页中胜点列的数据&#xff0c;找出胜点最高的召唤师&#xff0c;将召唤师姓名填入答案中 地址&#xff1a;https://match.yuanrenxue.cn/match/7 本题主要是考察字体的动态变化&#xff0c;同样也是从字体文件下手构造出映射关系就好&#xff0c;但本题…

【Training versus Testing】Positive intervals

GPT的解释&#xff1a; 这段话解释了在一维中&#xff0c;针对 "Positive intervals"&#xff08;正间隔&#xff09;假设类别 H 的成长函数 mq(N) 的计算方式&#xff0c;以及如何得出这个成长函数。 1. "Positive intervals" 指的是一维情境中的假设&a…

推荐书目:Python从入门到精通(文末送书)

目录 编辑推荐 内容简介 作者简介 前言/序言 本书特点 编辑推荐 “软件开发视频大讲堂”丛书是清华社计算机专业基础类零售图书畅销品牌之一。 &#xff08;1&#xff09;2008—2023年&#xff0c;丛书累计修订7次&#xff0c;销售400万册&#xff0c;深受广大程序员喜爱。…

Spring安全配置: 构建安全稳固的Java应用

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

2023国赛B题:多波束测线问题 评阅要点完整分析

本文所有分析仅代表个人观点&#xff0c;不代表官方&#xff0c;仅供参考 制作人&#xff1a;川川徒弟 demoo CSDN&#xff1a;川川菜鸟公众号&#xff1a;川川带你学AI 全文采用非编程做法  需要工具&#xff1a; geogebra、matlab工具箱   注&#xff1a; 本文全文不考虑…

前端Layui框架介绍

当涉及到前端UI框架时&#xff0c;Layui&#xff08;简称layui&#xff09;是一个备受欢迎的框架之一。在这篇博客中&#xff0c;我们将深入了解layui&#xff0c;包括其市场占有率、开发语言、使用场景、框架特点以及一些使用案例。 1. 市场占有率 Layui 是一款流行的前端UI框…

Mysql

视频链接 黑马Mysql 基础篇 通用语法及分类 DDL: 数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;DML: 数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQL: 数据查询语言&#xff0c;用来查询数据库中表的记录DCL: 数据控…

网络原理,了解xml, json,protobuffer的特点

目录 外卖服务器场景带入 大佬们通用的规范格式 一、&#x1f466; 外卖服务器场景 外面服务器沟通有很多模式——展示商家列表等等&#xff0c;只是其中一个&#xff0c;因此需要一个统一的规划了——不同应用程序&#xff0c;里面的自定义格式是不一样的&#xff0c;这样的…

icmp报文及用go实现

目录 一、概述 二、ICMP报文格式详解 2.1 什么是ICMP 2.2 ICMP报文格式 2.3 ICMP报文类型 2.4 实际报文举例 三、使用go实现icmp请求以及接收响应内容 一、概述 本文主要旨在学习icmp报文格式&#xff0c;以及通过go语言来实现ICMP发包。 二、ICMP报文格式详解 2.1 什…

练习:使用servlet显示试卷页面

试卷页面代码 在浏览器输入如下地址&#xff1a; http://localhost/examPageServlet 效果如下&#xff1a;

SV-6002T-P 网络对讲求助终端,立柱式智慧城市网络对讲求助终端,停车场出入口一键求助终端

SV-6002T-P 网络对讲求助终端&#xff0c;立柱式智慧城市网络对讲求助终端&#xff0c;停车场出入口一键求助终端 描述&#xff1a;SV-6002T是深圳锐科达电子有限公司的一款新型立柱型室外防水一键求助对讲终端&#xff0c;具有10/100M以太网接口&#xff0c;其接收网络的音频数…

基于Java+freemarker实现动态赋值以及生成Word文档

前言 有一个需求就是给定一个正确格式的 Word 文档模板&#xff0c;要求通过动态赋值方式&#xff0c;写入数据并新生成 该模板格式的 Word 文档。这很明显使用 Javafreemarker 方式来实现颇为简单。 一、导入依赖 <!-- freemarker --> <dependency><groupId…