C语言学习笔记之指针(二)

指针基础知识:C语言学习笔记之指针(一)-CSDN博客

目录

字符指针

 代码分析

指针数组

数组指针

函数指针

代码分析(出自《C陷阱和缺陷》)

函数指针数组

指向函数指针数组的指针

回调函数

qsort()


字符指针

一般用法:

特殊用法:

        上述代码的本质是把字符串 "hello bit." 的首字符的地址放到了pstr中。因为"hello bit."是一个常量字符串,是存放在代码段的数据,所以不能被修改,因此要用const 修饰来防止权限的放大。

 代码分析

        这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

指针数组

        指针数组是一种数组,数组内的每个元素的类型是指针类型。

用指针数组模拟实现一个二维数组:

#include <stdio.h>void print(int** arr, int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){printf("%d ", *(*(arr + i) + j)); //等价于 printf("%d ", arr[i][j]);//printf("%d ", arr[i][j]);}printf("\n");}}
int main()
{int arr1[] = { 1, 2, 3, 4, 5 };int arr2[] = { 2, 3, 4, 5, 6 };int arr3[] = { 3, 4, 5, 6, 7 };int* arr[] = { arr1, arr2, arr3 };print(arr, 3, 5);return 0;
}

注:模拟的二位数组在内存中并不一定连续存放,因此它不是真的二维数组。

数组指针

        我们已经熟悉:

  •   整形指针: int * pint; 能够指向整形数据的指针。
  •   浮点型指针: float * pf; 能够指向浮点型数据的指针。

        同理,数组指针就是指向数组的指针,即存放数组地址的指针变量。

写一个打印二维数组的函数,形参是数组指针:

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
void print_arr2(int(*arr)[5], int row, int col) //数组指针接收
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){printf("%d ", *(*(arr + i) + j));}printf("\n");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };print_arr1(arr, 3, 5);//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行(二维数组可以看成是一维数组的数组,即二维数组的每个元素是一个一维数组)//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收printf("\n");print_arr2(arr, 3, 5);return 0;
}

类型分析:

函数指针

        函数也是有地址的,顾名思义,函数指针就是指向函数的指针,即存放函数地址的指针变量。

        函数指针类型:函数返回值类型 (*指针变量名)(函数参数)

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

        根据上述代码的执行结果我们可以看出,函数名表示函数地址,&地址函数名也表示函数地址,即 函数名 == &函数名,所以 *(&函数名)== * 函数名 ,而*(&函数名)== 函数名,因此 函数名 == &函数名 == * 函数名

        首先,能存储地址,就要求pfun1或者pfun2是指针,那哪个是指针? 答案是:pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。pfun2先和()结合,表示pfun2是一个没有参数,返回类型是void* 的函数。

代码分析(出自《C陷阱和缺陷》)

(*(void (*)())0)();

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

利用 typedef 简化上述代码:

函数指针数组

        存放函数指针的数组就叫做函数指针数组。

        parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
        函数指针数组的用途:转移表

示例(计算器):

#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 x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表while (input){printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("输入操作数:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);}else if (input == 0){printf("程序退出\n");break;}elseprintf("输入有误\n");printf("ret = %d\n", ret);}return 0;
}

指向函数指针数组的指针

        顾名思义,指针指向的是一个每个元素是函数指针的数组。

回调函数

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

qsort()

        qsort()可以给任意类型(整型,浮点型,结构体等)的数据排序,但是需要我们按要求提供一个比较函数

用冒泡排序模拟实现一个类似 qsort() 的函数:

#include <stdio.h>void swap(char* p1, char* p2, size_t size) //交换arr[j],arr[j+1]这两个元素
{int i = 0;for (i = 0; i < size; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void*, const void*))
{int i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0) //两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int cmp_int(const void* p1, const void* p2) //回调函数
{return *(int*)p1 - *(int*)p2;
}//测试bubble_sort 排序整型数据
void test1()
{int arr[10] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(int), cmp_int);
}struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* p1, const void* p2) //回调函数
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}//测试bubble_sort 排序结构体数据(比较年龄)
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int cmp_stu_by_name(const void* p1, const void* p2) //回调函数
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}//测试bubble_sort 排序结构体数据(比较名字)
void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sizeof(struct Stu));bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{test1();test2();test3();return 0;
}

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

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

相关文章

新版AndroidStudio使用switch-case语句时出现Constant expression required错误

原因: 在新版的Android Studio中使用JDK17以上版本&#xff0c;会出现switch语句报错"Constant expression required"的问题&#xff0c;这是因为在JDK17中switch语句的条件表达式支持使用枚举类型&#xff0c;而这个特性还没有被支持。 解决方法: ①在gradle.prope…

UE4_动画基础_不同骨骼的动画重定向步骤

学习笔记&#xff0c;仅供参考&#xff01; 1、导入fbx外部资源&#xff0c;不包含动画&#xff0c;需要使用小白人动画资源。 2、打开Girl_Skeleton。发现骨骼数量不同&#xff0c;要想使用另一个骨骼的动画资源&#xff0c;需要进行不同骨骼的动画重定向。 3、打开小白人骨骼…

mmap函数小实验

mmap函数小实验 文章目的参数 length 不是页大小的整数倍会怎样&#xff1f;研究过程length结论 参数 offset 取不同的值时会怎样&#xff1f;研究过程offset 结论 参考链接 文章目的 本文是为了深入理解mmap的参数length与offset对mmap函数行为的影响&#xff0c;从而更好地理…

反转链表【java】

给定一个链表的头节点head反转链表 方法一&#xff1a;循环 1.定义三个指针&#xff1a; pre指针&#xff1a;刚开始指向空 prenull cur指针&#xff1a;刚开始指向head节点 curhead temp指针&#xff1a;保存cur指针指向节点的下一个节点 2. 不断循环改变相邻两个节点的指…

JavaWeb前端/后端开发规范——接口文档概述及YApi平台的使用

前言&#xff1a; 整理下笔记&#xff0c;打好基础&#xff0c;daydayup!!! 接口文档 什么是接口文档&#xff1f; 目前主流的开发模式为前后端分离式开发&#xff0c;为了方便前后端的对接&#xff0c;就需要使用接口文件进行统一规范。 接口文档记载什么信息&#xff1f; 1&…

第19天:信息打点-小程序应用解包反编译动态调试抓包静态分析源码架构

第十九天 本课意义 1.如何获取到目标小程序信息 2.如何从小程序中提取资产信息 一、Web&备案信息&单位名称中发现小程序 1.国内主流小程序平台 微信 百度 支付宝 抖音头条 2.小程序结构 1.主体结构 小程序包含一个描述整体程序的app和多个描述各自页面的page …

goland2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Goland 是一款由 JetBrains 公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于 Go 语言的开发。它提供了丰富的功能和工具&#xff0c;帮助开发者更高效地编写、调试和管理 Go 语言项目。 功能特点&#x…

Milvus 老友汇|RAG 场景、电商平台、AI 平台……如何用向量数据库构建业务方案?

近日&#xff0c;Milvus 老友汇Arch Meetup 在深圳圆满落幕。本次 Meetup 由 Milvus 社区携手 Shopee 共同举办&#xff0c;同时还邀请到来自 AWS、点石科技的技术专家&#xff0c;分享电商行业、RAG 场景、AI 平台等如何基于向量数据库构建业务方案。 以下是本次 Meetup 的重点…

OSPF---综合实验

1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两个环回&#xff0c;其他路由器均有一个环…

HTML 入门

HTML 简介 1. 什么是 HTML&#xff1f; 全称&#xff1a;HyperText Markup Language&#xff08;超文本标记语言&#xff09;。 超文本&#xff1a;暂且简单理解为 “超级的文本”&#xff0c;和普通文本比&#xff0c;内容更丰富。 标 记&#xff1a;文本要变成超文本&…

PLC扩展更自由,钡铼IOy系列模块实现DI/DO/AI/AO任意组合

随着工业自动化的不断发展&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为工业控制领域的核心设备&#xff0c;扮演着至关重要的角色。而钡铼IOy系列模块作为PLC的重要扩展设备&#xff0c;不仅实现了DI&#xff08;数字输入&#xff09;、DO&#xff08;数字输出…

TOP100 二分法

写在前面&#xff1a;二分法用在有序序列上 1.35. 搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示…

Ubuntu20从0开始选择合适版本手动安装cuda,torch-geometric,jax

一个全新的ubuntu20台式机&#xff0c;在Additional Drivers安装nvidia-470-server&#xff08;一开始安装450&#xff0c;cunda版本只能到11.0&#xff0c;torch有些库用不了&#xff0c;可以直接切换点击Apply Changes重启就行&#xff09; nvidia-smi查看CUDA Version可到…

全球最新国内外18个热门风景视频素材网站推荐

寻找最新的高清风景视频素材&#xff1f;这里有国内外共18个热门网站&#xff0c;精心整理供您选择。 国内资源&#xff1a; 蛙学网&#xff1a;免费提供多种无版权视频素材&#xff0c;资源丰富。新GG网&#xff1a;需QQ登录&#xff0c;提供丰富的视频模板&#xff0c;通过…

vue快速入门(十七)v-model数据双向绑定修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-model.trim 自动去除首尾空格v-model.number 自动转换成数字类型 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" con…

基于ADB的Scrcpy实现电脑控制手机

Scrcpy是一个开源的&#xff0c;基于ADB&#xff08;Android 调试桥&#xff09;的手机到电脑上的投屏操控的实现&#xff0c;本文将介绍如何搭建开发环境&#xff0c;使得在Windows系统中去控制投屏的安卓手机。 1. 安装投屏软件 下载Scrcpy软件到电脑上&#xff0c;该软件中…

YoloV9实战:从Labelme到训练、验证、测试、模块解析

模型实战 训练COCO数据集 本次使用2017版本的COCO数据集作为例子&#xff0c;演示如何使用YoloV8训练和预测。 下载数据集 Images: 2017 Train images [118K/18GB] &#xff1a;http://images.cocodataset.org/zips/train2017.zip2017 Val images [5K/1GB]&#xff1a;htt…

【电控笔记2.2】电流回路+延迟效应

延迟效应的来源以及影响 数字控制系统的delay: 5.4节有介绍T0=0.5TS 低通滤波器的时间常数? 滤波器的传递函数与性能参数

【多线程】阻塞队列 | put()方法 | take()方法 | 生产者-消费者模式 |实现阻塞队列

文章目录 阻塞队列1.生产者-消费者模式生产者消费者模型的意义&#xff1a;1.解耦合2.削峰填谷&#xff1a; 2.阻塞队列的使用BlockingQueue 3.实现阻塞队列唤醒&#xff1a;使用阻塞队列实现生产者消费者模型 阻塞队列 阻塞队列是一种特殊的队列&#xff1a; 1.是线程安全的。…

Vue 阶段练习:记事本

将 Vue快速入门 和 Vue 指令的学习成果应用到实际场景中&#xff08;如该练习 记事本&#xff09;&#xff0c;我们能够解决实际问题并提升对 Vue 的技能掌握。 目录 功能展示 需求分析 我的代码 案例代码 知识点总结 功能展示 需求分析 列表渲染删除功能添加功能底部统计…