日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C語言中回調函數和qsort函數的用法詳解_C 語言

作者:從未止步.. ? 更新時間: 2022-09-09 編程語言

回調函數

通過函數指針調用的函數,如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。

回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。

舉例:

#include<stdio.h>
void menu()
{
    printf("********************************\n");
    printf("**    1.and        2.sub      **\n");
    printf("**    3.mul        4.div      **\n");
    printf("********************************\n");
}
int add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}
int sub(int x, int y)
{
    int z = 0;
    z = x - y;
    return z;
}
int mul(int x, int y)
{
    int z = 0;
    z = x * y;
    return z;
}
int div(int x, int y)
{
    int z = 0;
    z = x / y;
    return z;
}
void Calc(int(*pf)(int, int))//int(*pf)(int, int)等于add,只不過選用不同的方式進行調用
{
    int x = 0;
    int y = 0;
    printf("請輸入兩個操作數:>");
    scanf_s("%d%d", &x, &y);
    printf("%d\n", pf(x, y));//通過指針對add函數進行調用,而不是像之前那樣使用函數名進行調用
}
int main()
{
    int input = 0;
    do
    {
        menu();
        printf("請選擇:>");
        scanf_s("%d", &input);
        switch (input)
        {
        case 1:
            Calc(add);//將add函數的地址傳遞過去,這里的add函數為回調函數
            break;
        case 2:
            Calc(sub);
        case 3:
            Calc(mul);
        case 4:
            Calc(div);

        }
    } while (input);
}

指向函數指針數組的指針

指向函數指針數組的指針是一個指針,指向一個數組,數組的元素都是函數指針;

如何定義?

    int arr[10] = { 0 };//整型數組
    int(*p)[10] = &arr;//取出數組的地址
    int (*pf)(int, int);//函數指針
    int(*pfarr[4])(int, int);//pfarr是一個數組,函數指針的數組
    int(*(*ppfarr)[4])(int, int) = &pfarr;//ppfarr指向函數指針數組的指針
    //pfarr是一個數組指針,指針指向的數組有4個元素
    //指向的數組的每個元素的類型是函數指針int(*)(int,int)

void*

可以用來接收任何類型數據的地址,別名萬能指針

既然可以存放任何類型的地址,那么是不是也可以解引用訪問存放的值?

下面我們通過示例:

#include<stdio.h>
int main()
{
    int a = 10;
    void* p = &a;
    printf("%d\n", *p);
}

通過輸出結果我們發現,程序并沒有被正確的運行,而是告訴我們,我們進行了非法間接尋址。

那么為什么會出現這樣的現象呢?

原因是void*雖然可以接受任意類型的地址,但它自己本身的類型是空類型,那么在解引用操作的時候,系統并不知道它的類型,因此不知道需要分配給其幾個字節,指針類型決定了它的字節大小。

因此,void*不能進行解引用操作

那么可以進行++/–操作嗎?

我們通過實例進行驗證一下:

#include<stdio.h>
int main()
{
    int a = 10;
    void* p = &a;
    p++;
}

程序依然沒有正確運行,編譯器指出了錯誤的原因:void未知的大小,和上面一樣的道理,我們并不清楚此時存放在void里面的數據是什么類型,自然也不知道它所占據內存空間的大小,因此步長是無法確定的。

因此void*也不可以進行++/–操作

qsort(qulick sort)-庫函數

適用的場景:適用于對某一組數據進行快速排序

qsort(s,sz,sizeof(s[0]),cmp_stu_by_name);
//第一個參數s:待排序數組的首元素地址
//第二個參數sz:待排序數組的元素個數
//第三個參數sizeof(s[0]):待排序數組的每個元素的大小,單位是字節
//第四個參數:是函數指針,比較兩個元素的所用函數的地址
//這個函數使用者自己實現函數指針的兩個參數是:待比較的兩個元素的地址

舉例:

常規方法:冒泡排序:

#include<stdio.h>
int main()
{
    int arr[10] = { 9,2,3,1,4,5,7,6,0,91 };
    int i, j=0,temp;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (i = 0; i < sz-1; i++)//決定需要比較多少次
    {
        for (j=0; j < sz-1-i; j++)
        {
            if (arr[j+1] > arr[j])
            {
                temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }    
        }
    }
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

輸出:

91 9 7 6 5 4 3 2 1 0

但是,這種方法的局限性非常大,執行效率也不高。

因此在進行數據類型的排序問題時,我們可以選擇qsort函數:

下面我們就來學習qsort函數:

//int (*cmp)(const void *,const void *);
qsort(*s, n, sizeof(s[0]), cmp);

其中第一個參數s是一個地址,即參與排序的首地址; n是需要排序的數量; sizeof(s[0])則是每一個元素占用的空間大?。?/p>

指向函數的指針,用于確定排序的順序。

sz=sizeof(arr)/sizeof(arr[0])
qsort(a, sz,arr[0], cmp);
//其中cmp函數應寫為:
int cmp(const void *a, const void *b)//void*可接受任意類型的數據
{
    return *(int*)a - *(int*)b; //由小到大排序
    //return *(int *)b - *(int *)a; 由大到小排序
}

對于整形數據的比較實現過程:

#include<stdio.h>
#include<stdlib.h>#qsort的頭文件
int cmp_int(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
    return *(int*)e1 - *(int*)e2;
}
int main()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr,sz, sizeof(arr[0]), cmp_int);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);//整形打印%d
    }
    return 0;
}

輸出:

0 1 2 3 4 5 6 7 8 9

對于浮點型數據的比較實現過程:

int cmp_float(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
//由于cmp_float函數的返回類型是int,因此,需要進行轉化
{
     //可用if分支語句
    /*if (*(float*)e1 == *(float*)e2)
        return 0;
    else if (*(float*)e1 > *(float*)e2)
        return 1;
    else
        return 0;*/
        //也可用return直接返回
    return ((int) * (float*)e1 - *(float*)e2);
}
int main()
{
    float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0 };
    int sz = sizeof(f) / sizeof(f[0]);
    qsort(f,sz, sizeof(f[0]), cmp_float);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%f ", f[i]);//浮點型打印%f
    }
    return 0;
}

對于結構體類型數據的實現過程:

結構體類型和整形浮點型在排序的時候有略微區別,結構體類型并不能直接進行比較,而要按照某一變量,例如:名字,年齡等等

按照年齡比較:

#include<stdio.h>
#include<stdlib.h>
struct stu//定義一個結構體
{
	char name[20];
	int age;
};

int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;//告訴編譯器你想用什么樣的方式進行排序
}
int main()
{
	struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
	int sz = sizeof(s) / sizeof(s[0]); 
	qsort(s,sz, sizeof(s[0]), cmp_s_stu);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i]);#字符串型,以%s進行打印
	}
	return 0;
}

按照名字進行比較:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[20];
	int age;
};

int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
	return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);//比較名字就是比較字符串
	//注意字符串在比較大小的時候,不能直接用加減法進行比較,而要用strcmp()函數
}
int main()
{
	struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
	int sz = sizeof(s) / sizeof(s[0]); 
	qsort(s,sz, sizeof(s[0]), cmp_s_stu);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i]);#字符串型,以%s進行打印
	}
	return 0;
}

原文鏈接:https://blog.csdn.net/m0_64365419/article/details/125791909

欄目分類
最近更新