首页 > 其他分享 >指针进阶(3)————玩转指针

指针进阶(3)————玩转指针

时间:2023-05-15 16:31:52浏览次数:31  
标签:进阶 int void struct char width 玩转 函数 指针

指针进阶

内容不多,但面面俱到,都是精华

指针进阶(3)————玩转指针_数组

1.回调函数:

2.详解qsort函数参数:

3.模拟实现qsort函数

回调函数就是,把一个函数的地址,放在函数指针中,然后将该指针作为一个参数,传到 另一个函数中,在这个函数内部使用了外部写好的一个函数. 举一个例子,看完你一定明白了例子:void menu(void) { printf("*************************\n"); printf("**1.Add 2.Sub**********\n"); printf("**3.Mul 4.Div**********\n"); printf("********0.exit***********\n"); } 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 cal(int (*pf)(int a, int b))//函数指针 { //这里面存放的是你需要选用的函数的地址 int a = 0; int b = 0; printf("请输入两个数:"); scanf("%d%d", &a, &b); printf("结果为%d\n", pf(a, b)); } int main() { int input = 0; do { menu(); printf("请选择:->\n"); scanf("%d", &input); switch (input) { case 1: cal(Add); //在一个函数内部调用另一个已经写好的函数 break; case 2: cal(Sub); break; case 3: cal(Mul); break; case 4: cal(Div); break; case 0: printf("退出\n"); break; default: printf("选择错误\n"); break; } } while (input); }简单的说,就是在一个函数内部调用另一个已经写好的函数 图解如上:理解回调函数后,下面引出一个函数,叫做 qsort 函数函数参数如下void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));参数1 待排序数组首元素地址 参数2 元素个数 参数3 元素大小--单位是字节 参数4 函数指针,比较两个元素的方法和函数的地址,该函数自己实现

详解qsort函数参数: 1.为什么指针是void类型呢? void指针,可以理解成一个万能桶,什么类型的地址都能放到void的指针里面 举一个简单的例子:int a =10 ; void*p = &a; char ch ='w' ; p = & ch ; 编译该段代码可以发现,未发生错误注意看,调试过程中,指针p存放的地址由a的地址变成了ch的地址,证明什么类型的地址都能放到void*的指针里面由于void是无类型指针,所以void类型指针不能进行加减操作,不能进行解引用操作,想要进行这些操作,需要把void类型强制类型转成所需要的类型才能操作编译无法通过;2.函数指针,比较两个元素的方法和函数的地址,该函数自己实现什么意思呢?直接上代码: 下面排序一个 int 类型数组的元素int cmp_int(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } 该函数实现的是 两个元素 比较的方法 int main() { int arr[] = { 2,3,5,7,1,4,9,6,8 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_int); 参数1:数组首元素地址,参数2:元素个数 参数3:每个元素大小(单位字节) 参数4:函数指针 for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } }由于 void指针无类型,故在比较函数内部,需要把void指针强制类型转化为所需要的类型可以想象 qsort 函数是拿到比较的方法后,通过这个方法比较完其他的所有元素代码运行结果如下: 非常方便快捷得出结果再来一个例子,假如要排序结构体呢?int cmp_struct(const void* e1, const void* e2) { return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name); //以名字来对结构体进行排序 } int main() { struct stu student[3] = { {"张三",20},{"李四",19},{"王五",18}}; int sz = sizeof(student) / sizeof(student[0]); qsort(student, sz, sizeof(student[0]), cmp_struct); for (int i = 0; i < sz; i++) { printf("%s %d\n", student[i].name, student[i].age); } }对结构体进行排序时,根据需求,是按名字字母顺序排序还是按年龄排序 对于结构体排序来说 , e1需要强制类型转化为结构体类型指针 按名字排序的结果如上:总结: qsort函数参数的理解重点在于函数指针 就是在外部写好一个函数,将该函数的地址作为参数传给qsort函数, 外部写好的函数就是 回调函数 。

3.自己编写冒泡排序函数,要求能对任意类型的数据进行排序 (仿照qsort参数) 代码剖析: 要自己实现冒泡排序,基本的框架和思想是不变的,两层循环进行排序下面开始深入剖析每段代码的含义:1.主函数实现int main() { char arr[][15] = { "hello","world","i love china" }; int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), cmp_char); for (int i = 0; i < sz; i++) { printf("%s\n",arr[i]); } }2.冒泡排序函数实现,具体请看下面void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2)) { //base是数组首元素地址 函数指针,存放的是cmp比较函数的地址 int i = 0; int j = 0; for (i = 0; i < sz -1; i++) { for (j = 0; j < sz - i -1; j++) { if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0) { //把void*指针强制类型转化成char*指针,因为不知道未来要排序的元素是什么类型 // 那就统一变成char*,是一个字节,加上宽度,就能知道每个元素是几个字节了 //比较函数 base+每个元素的大小(宽度width) 意味着移动到该元素下 //j =0时,比较第一个和第二个元素,j=1时,比较第二个和第三个元素 swap( (char*)base + width * j, (char*)base + width * (j + 1), width); //交换 这里不仅要传两个要交换的元素的地址过去,还要知道这两个元素有多长,就需要传width //否则无法交换完成 } } } }3.交换函数实现void swap(char* buf1, char* buf2 ,int width) { //这里用char*的指针来接受,每移动一次就是1个字节 int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; //每执行交换一次,指针往后移继续交换,直到把两个数组里面的元素都交换为止 } }width 已经在主函数计算过,现在直接使用即可4.cmp_char函数实现int cmp_char(const void* e1, const void* e2) { return strcmp( (char*)e1 , (char*)e2 ); } 结果如上:假如不想比较字符串了,想对结构体进行排序: 1.重新写一个函数,这个函数实现的是比较结构体的方法: 如下:struct stu { char name[20]; int age; };先初始化结构体int cmp_struct_by_age(const void* e1, const void* e2) { //return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name); //以名字来对结构体进行排序 return ((struct stu*)e1)->age - ((struct stu*)e2)->age; //以年龄对结构体进行排序 }自定义比较函数,通过年龄来比较,先把e1和e2强转为结构体类型指针,通过结构体指针直接访问年龄。int main() { struct stu student[3] = { {"张三",19},{"李四",20},{"王五",18}}; int sz = sizeof(student) / sizeof(student[0]); bubble_sort(student, sz, sizeof(student[0]), cmp_struct_by_age); for (int i = 0; i < sz; i++) { printf("%s %d\n", student[i].name, student[i].age); } }设置好函数比较方法后,直接调用即可下面是运行结果说到这里,把全部代码贴出来,理解之后,你可以运行一下看看效果struct stu { char name[20]; int age; }; void swap(char* buf1, char* buf2, int width) { //这里用char*的指针来接受,每移动一次就是1个字节 int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2)) { //base是数组首元素地址 int i = 0; int j = 0; for (i = 0; i < sz - 1; i++) { for (j = 0; j < sz - i - 1; j++) { if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0) { //把void*指针强制类型转化成char*指针,目的是不知道未来要排序的元素是什么类型 // 那就统一变成char*,是一个字节,加上宽度,就能知道每个元素是几个字节了 //比较函数 base+每个元素的大小(宽度width) 意味着移动到该元素下 //j =0时,比较第一个和第二个元素,j=1时,比较第二个和第三个元素 swap((char*)base + width * j, (char*)base + width * (j + 1), width); //交换 这里不仅要传两个要交换的元素的地址过去,还要知道这两个元素有多长,就需要传width //否则无法交换完成 } } } } int cmp_struct_by_age(const void* e1, const void* e2) { //return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name); //以名字来对结构体进行排序 return ((struct stu*)e1)->age - ((struct stu*)e2)->age; //以年龄对结构体进行排序 } int main() { struct stu student[3] = { {"张三",19},{"李四",20},{"王五",18}}; int sz = sizeof(student) / sizeof(student[0]); bubble_sort(student, sz, sizeof(student[0]), cmp_struct_by_age); for (int i = 0; i < sz; i++) { printf("%s %d\n", student[i].name, student[i].age); } }关注我,给你更多惊喜。

标签:进阶,int,void,struct,char,width,玩转,函数,指针
From: https://blog.51cto.com/u_15818575/6279761

相关文章

  • 40+JavaScript进阶单行代码
    数组//生成数组0-99的数组//方案1constcreateArr=(n)=>Array.from(newArray(n),(v,i)=>i);letarr=createArr(100);console.log(arr);//方案2constcreateArr=(n)=>newArray(n).fill(0).map((v,i)=>i)......
  • 2、c++中的指针参数传递和引用参数传递
    指针参数传递本质上是值传递。值传递的过程中,被调函数的形式参数作为被调函数的局部变量处理,会在栈中开空间用以存放由主调函数传递的实际参数,从而形成了实参值得一个副本。而值传递的特点是被调函对形参的任何修改都不会影响实参值。(如果想通过指针参数来修改主调函数的相关变量或......
  • 算法刷题系列之移除元素:快慢指针技巧
    题目+日期移除元素2023年5月14日17点50分基础知识暴力解法这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素,第二个for循环更新数组。双指针法(快慢指针法)通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。定义快慢指针快指针:寻找新数组的元......
  • 【2023 · CANN训练营第一季】进阶班笔记1
    1.在AscendCL中,关于媒体数据处理V1版本中的内存申请/释放接口acldvppMalloc/acldvppFree:该接口主要用于分配内存给Device侧媒体数据处理时使用,申请的大页内存满足数据处理的要求(例如,内存首地址128字节对齐)。调用该接口申请内存后,如果内存不使用,需及时调用acldvppFree接口释放内存频......
  • unique_ptr智能指针介绍
    unique_ptr是C++标准库提供的智能指针之一,具有以下特点:独占所有权:unique_ptr独占指向对象的所有权,确保在任何时候只有一个unique_ptr可以指向同一个对象。当unique_ptr被销毁或转移所有权时,它会自动释放指向的对象,无需手动删除。轻量高效:unique_ptr是一种轻量级的智能指针,通......
  • c++进阶项目—基于多态的职工管理系统
    一、管理系统需求公司中职工分为三类:普通员工、经理、老板显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责1、普通员工职责:完成经理交给的任务2、经理职责:完成老板交给的任务,并下发任务给员工3、老板职责:管理公司所有事务管理系统中需要实现的功能如下:1、退出管理程序:退......
  • 指针进阶(2)——玩转指针
    今天内容不多,但都是精华。1.数组参数和指针参数2.函数指针2.1笔试题3.函数指针数组1.数组参数和指针参数例1:一维数组传参voidtest(intarr[]){}voidtest(intarr[10]){}voidtest(int*arr){}voidtest2(int*arr2[20]){}voidtest2(int**arr2){}传参的时候,*arr2......
  • 【Leetcode算法01】双指针Two Pointers
    TableofContents同向双指针剑指offer05.替换空格相向双指针344.反转字符串206.反转链表151.翻转字符串里的单词19.删除链表的倒数第N个节点160.相交链表142.环形链表II15.三数之和18.四数之和快慢双指针27.移除元素Solutions27.移除元素力扣题......
  • 双指针——最长连续不重复子序列(例)
    给定一个长度为n的整数序列,找出最长的不包含重复的数的连续区间,输出它的长度。数据范围: 输入样例:512235输出样例:3 #include<iostream>//C++标准库中的头文件.用于控制台输入和输出。#include<cstring>//用于处理字符串的函数和操作#include<algorithm>/......
  • Scanner进阶使用
    1. (if的使用会在后续学习)其他类型像int,float型等就把相应的都变成nextInt()/hasNextInt()  nextFloat()/hasNextFloat()(Scanner其实就是和键盘相关联,可以输入一些东西,然后运行。)......