C语言---深入指针(4)(一)

简介: C语言---深入指针(4)

回调函数

//回调函数就是通过函数指针调用的函数
//这个在之前的转移表-计算器里面很明显,通过函数指针数组内的函数指针进行函数的调用
//
// 
// 将这四段代码分装成一个函数,一个代码将这4个问题都解决
int Add(int x, int y)
{
    return x + y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mull(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}
void menu()
{
    printf("**************************************\n");
    printf("**********   1.add    2.sub***********\n");
    printf("*********    3.mull   4.div***********\n");
    printf("*********    0.exit      *************\n");
    printf("**************************************\n");
}
void Calc(int (*pf)(int, int))//因为传过来的是函数的地址,那么就要用函数指针进行接收
{
    int x = 0, y = 0,ret=0;
    printf("请输入两个操作数");
    scanf("%d %d", &x, &y);
    ret = pf(x, y);//当pf接收到的是加法函数的地址的时候,我们直接用pf来调用
    //对于函数名就是函数的地址,用指针变量存起来的就是函数地址,那么指针变量的名字就是函数变量的地址
    //那么可以直接用指针变量的名字进行调用
    printf("%d\n", ret);
}
int main()
{
    int ret = 0;
    int input = 0;
    int x = 0, y = 0;
    do
    {
 
        menu();//菜单
        printf("请选择:");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            Calc(Add);    //将加法函数的地址传过去--函数名是地址       
            break;
        case 2:
            Calc(Sub);            
            break;
        case 3:
            Calc(Mull);            
            break;
        case 4:
            Calc(Div);
 
            break;
        case 0:
            printf("退出计算器\n");
            break;
        default:
            printf("选择错误,重新选择\n");
            break;
        }
    } while (input);
    return 0;
}
 //这4个代码的差异就是运算的方式不同
//
// Calc是中间商,Add这些计算的函数是回调函数
// 
// 通过函数调用另一个函数进行调用
//
//当你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其
//所指向的函数时,被调用的函数就是回调函数
//这个代码里面回调函数就是Add,Sub,Mull,Div

回忆冒泡函数

//回忆冒泡排序---对一组整数进行排序
//void bubble_sort(int arr[], int sz)//接受一个整型数组
//{
//    //趟数
//    for (int i = 0; i < sz-1; i++)
//    {
//        //一趟内部的两两比较
//        for (int j = 0; j < sz - 1 - i; j++)
//        {
//            if (arr[j] > arr[j + 1])
//            {
//                int tmp = arr[j];
//                arr[j] = arr[j + 1];
//                arr[j + 1] = tmp;
//            }
//        }
//    }
//}
//print(int arr[], int sz)
//{
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}
//int main()
//{
//    int arr[] = { 3,1,7,9,4,2,6,8,0 };
//    //进行排序--升序
//    int sz = sizeof(arr) / sizeof(arr[0]);
//
//    //冒泡排序核心思想,两两数字进行比较,不满足我们要求的顺序的话我们可以对其进行交换
//    //满足我们要求的顺序的话我们就换下一对数字进行比较
//    
//    //冒泡排序进行排序
//    bubble_sort(arr,sz);
//    print(arr,sz);
//    return 0;
//}
//对于冒泡排序的话,只能排序整型数组
//对于其他类型的数组无法排序
//字符串数组、字符数组、结构体数组、浮点数数组

strcmp----字符串的大小比较

/字符串的比较
//应该使用strcmp进行比较
//int strcmp(const char* str1, const char* str2);
//char str1[]="Hello";
//char str2[]="hello";
//strcmp(str1, str2));
// 如果 strcmp 返回值大于 0,
// 则表示第一个字符串(str1)在比较中大于第二个字符串(str2)。
// 

a,b,c,d,c,e,f

a,b,c,q

因为d的ASCII小于q的,所以第一行的字符串就小于第二行的字符串

strcmp比较的不是长度,比较的是对应位置的字符ASCII大小的

qsort的结构

而接下来介绍的qsort能适用于任意类型的数据的排序
void qsort(void* base,//指向待排序数组第一个元素的指针
            size_t num, //base指向的数组中的元素个数
            size_t size, //base指向的数组中每个元素的大小
            int(*com)(const void*, const void*)//函数指针--传递函数的地址
            //
            );

利用qsprt进行整型数组的排序

//对于冒泡排序比较不同的类型的数据时,两个循环没有什么差异,但是对于不同的数据进行比较就有着差异
//对于整数是进行比较大小,但是并不是所有的数据能能用比'>''<'进行比较
 
//既然不同数据的元素比较是有差异的,那么就直接把两个元素比较的代码抽离出来 
//谁要调用qsort来进行比较排序,那就让谁提供比较的函数
 
 
//下面的函数比较的是p1指向的元素和p2指向的元素
//对函数
//p1指向的元素比p2指向的元素小的时候,返回的是小于0的数
//p1指向的元素等于p2指向的元素的时候,返回的是0
//p1指向的元素比p2指向的元素打的时候,返回的是大于0的数
 
//咱么为了使用qsort函数,我们必须提供一个比较2个整型的比较函数
//格式int cmp_int(const void*p1, const void*p2),那么这个函数的名字就是qsort函数的第四个条件
//int cmp_int(const void*p1, const void*p2)
//{
//    //p1就指向了这个数组里面的3,p2指向的就是1
//    /*if(*p1>*p2)*///不能这么写,因为在函数的参数里面,p1的类型是void*,p2是void*
//    //void *指针不能进行解引用操作
//    //将p1的类型强制转换成int*,再进行解引用就行了     *(int*)p1
//    //比较的三种情况
//    if (*(int*)p1 > *(int*)p2)
//    {
//        return 1;
//    }
//    else if (*(int*)p1 < *(int*)p2)
//    {
//        return -1;
//    }
//    else
//    {
//        return 0;
//    }
//
//}
//void print(int arr[], int sz)
//{
//    for (int i = 0; i < sz; i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}
test这个函数测试qsort来排序整型数据
//void test1()
//{
//    int arr[] = { 3,1,7,9,4,2,6,8,0 };
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    qsort(arr, sz, sizeof(arr[0]), cmp_int);
//    //(数组首元素地址,数组长度,每个元素字节大小,进行比较大小的函数)
//    print(arr, sz);//打印排序完后的数组
//}
//int main()
//{
//    test1();
//    return 0;
//}
 
//提供想要排序的数组,计算数组元素个数
//再利用qsort进行排序
//再利用qsort进行排序的时候,要提供数组首元素的地址、数组长度、数组每个元素的字节大小、
//最后再提供一个比较的函数的函数名,这个函数有固定的写法
//int cmp_int(const void*p1, const void*p2)
//因为p1和p2的类型都是void*,不能对其进行解引用进行比较,
//所以我们必须将其强制类型转换,(int*)p1
//再对其进行解引用*(int*)p1
//再将*(int*)p1与*(int*)p2进行比较
//前者大于后者就返回大于0的数
//前者小于后者就返回小于0的数
//前者等于后者就返回0
 
//对于qsort函数来说,我们只需要额外构建一个比较函数就能利用qsort进行快速排列

对于qsort函数来说,我们只需要额外构建一个比较函数就能利用qsort进行快速排列

//对下面代码进行简化
// test1
int cmp_int(const void* p1, const void* p2)
{
 
    return *(int*)p1 - *(int*)p2;
}
//因为这里比较的是整数,那么我们直接作差返回
void print(int arr[], int sz)
{
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
//test这个函数测试qsort来排序整型数据
void test1()
{
    int arr[] = { 3,1,7,9,4,2,6,8,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    //(数组首元素地址,数组长度,每个元素字节大小,进行比较大小的函数)
    print(arr, sz);//打印排序完后的数组
}
int main()
{
    test1();
    return 0;
}

利用qsprt进行结构体的排序

/*void qsort(void* base,//指向待排序数组第一个元素的指针
            size_t num, //base指向的数组中的元素个数
            size_t size, //base指向的数组中每个元素的大小
            int(*com)(const void*, const void*)//函数指针--传递函数的地址
            //
            );*/
//利用qsort函数对结构体进行排序
struct Stu//创建一个结构体
{
    char name[20];
    int age;
};
 
//按照名字比较两个结构体的数据
int cmp_stu_by_name(const void* p1, const void* p2)//p1和p2分别指向数组中的结构体
{
    //先将p1强制类型转换
    //(struct Stu*)p1---结构体指针-----不能直接指向成员  错误写法:(struct Stu*)p1->
    //要先括起来,将强制类型转换后的结构阔起来,才能找到成员
 
    //((struct Stu*)p1)->name, ((struct Stu*)p2)->name
    // 
 
     //两个名字是字符串,字符串比较是采用strcmp函数的
    return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
 
    //因为strcmp函数的返回值和我们期望的qsort的第四个比较函数的返回值一样的,
    //所以我们直接将strcmp的值返回
    //strcmp要包含头文件的include <string.h>
}
void print(struct Stu arr[], int sz)
{
    for (int i = 0; i < sz; i++)
    {
        printf("%s ", arr[i].name);
    }
}
void test2()
{
    struct Stu arr[] = { {"zhangsan",20},{"lisi",35},{"wangwu",18}};//给了三个人的信息 
    //利用qsort对数组内的信息进行排序
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//传递的函数是进行比较两个结构体对象
        //但是我们现在是按照名字比较还是年龄比较呢?
    print(arr, sz);
}
 
int main()
{
    test2();
    return 0;
}
//按照年龄排序
struct Stu//创建一个结构体
{
    char name[20];
    int age;
};
void print(struct Stu arr[], int sz)
{
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i].age);
    }
}//打印结果:18 20 35
void cmp_stu_by_age(const void* p1, const void* p2)
{
    //return strcmp(((struct Stu*)p1)->age, ((struct Stu*)p2)->age);
    //因为age是两个整数,所以不需要用strcmp了
    return ((struct Stu*)p1)->age-((struct Stu*)p2)->age;
    //将他们的值进行大小比较,直接返回
    //前者大于后者直接返回大于0的数
    //前者小于后者直接返回小于0的数
    //前者等于后者直接返回0
 
 
 
}
void test3()
{
    struct Stu arr[] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
    print(arr, sz);
 
}
int main()
{
    test3();
    return 0;
}


C语言---深入指针(4)(二)https://developer.aliyun.com/article/1544403

相关文章
|
2月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
42 1
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
52 0
|
12天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
20 2
|
2月前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
2月前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
2月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
2月前
|
C语言
C语言指针(3)
C语言指针(3)
14 1