【C语言】qsort详解——能给万物排序的神奇函数

news2025/2/23 1:57:26

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html

🎁代码托管:qsort的使用和模拟实现 · a96cdd4 · 黄灿灿/C语言 - Gitee.com

⚙️操作环境:Visual Studio 2022

目录

一、引言

二、qsort函数介绍

1.函数原型

2.参数说明

2.1比较函数

3.使用示例

3.1对一维数组进行排序 

3.2对结构体进行排序 

4.让我们尝试用更简单的方式来说明如何使用 qsort 函数。

步骤 1: 定义比较函数

步骤 2: 调用 qsort 函数

5.进阶 

5.1 解释:

1. 参数:

2. 返回值:

2. 实现细节:

5.2 为什么自动排序?

1. 减法操作

2.qsort 排序机制

3. 实现细节

三、模拟实现qsort函数

使用bubble_sort函数

四、总结

五、共勉


一、引言

在C语言中,qsort函数是一个非常强大且灵活的排序工具,它能够处理各种不同类型的数据结构,包括基本类型数组、字符串、二维数组甚至是结构体。本篇文章将详细介绍qsort函数的工作原理以及如何使用它来进行排序。

二、qsort函数介绍

qsort函数是C标准库中的一个通用排序函数,它的原型定义在stdlib.h头文件中。qsort函数之所以被称为“快速排序”函数,是因为它通常采用了一种高效的排序算法——快速排序算法来实现排序过程。

来看看qsort - C++ Reference上的介绍:

1.函数原型

void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *));

2.参数说明

  • base:指向待排序数组的指针。(谁需要排序)
  • num:数组中元素的数量。(需要排序的数量/有多少数需要排序)
  • size:数组中每个元素的字节数。(每个元素有多大)
  • compar:指向一个比较函数的指针,该函数用于确定排序顺序。(比较函数的指针)

2.1比较函数

比较函数compar接受两个void *类型的参数,并返回一个int类型的值。如果第一个参数应该排在第二个参数之前,则返回负数;如果两者相等,则返回0;如果第一个参数应该排在第二个参数之后,则返回正数。

3.使用示例

让我们通过几个例子来看看如何使用qsort函数。

3.1对一维数组进行排序 

#include <stdio.h>
#include <stdlib.h>

int compar(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int main() {
    int arr[] = {3, 1, 5, 9, 7, 6, 4, 8, 0, 2};
    size_t num = sizeof(arr) / sizeof(arr[0]);
    size_t sz = sizeof(arr[0]);

    qsort(arr, num, sz, compar);

    for (int i = 0; i < num; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

3.2对结构体进行排序 

按年龄排序
#include <stdio.h>
#include <stdlib.h>

struct Stu {
    char name[20];
    int age;
};

int compar_Stu_age(const void *p1, const void *p2) {
    return ((struct Stu *)p1)->age - ((struct Stu *)p2)->age;
}

int main() {
    struct Stu s[] = {{"zhangsan", 20}, {"lisi", 30}, {"wangmazi", 25}};
    size_t num = sizeof(s) / sizeof(s[0]);
    size_t sz = sizeof(s[0]);

    qsort(s, num, sz, compar_Stu_age);

    for (int i = 0; i < 3; i++) {
        printf("%s, %d\n", s[i].name, s[i].age);
    }

    return 0;
}
按姓名排序
int compar_Stu_name(const void *p1, const void *p2) {
    return strcmp(((struct Stu *)p1)->name, ((struct Stu *)p2)->name);
}

int main() {
    struct Stu s[] = {{"zhangsan", 20}, {"lisi", 30}, {"wangmazi", 25}};
    size_t num = sizeof(s) / sizeof(s[0]);
    size_t sz = sizeof(s[0]);

    qsort(s, num, sz, compar_Stu_name);

    for (int i = 0; i < 3; i++) {
        printf("%s, %d\n", s[i].name, s[i].age);
    }

    return 0;
}

4.让我们尝试用更简单的方式来说明如何使用 qsort 函数。

假设我们有一个整数数组,我们想要按升序排序这个数组。首先我们需要定义一个比较函数,然后调用 qsort 函数。

步骤 1: 定义比较函数

比较函数用来告诉 qsort 如何比较数组中的元素。对于整数数组,我们可以这样定义比较函数:

int compare(const void *a, const void *b) {
    if (*(int *)a < *(int *)b) {
        return -1;  // a 小于 b
    } else if (*(int *)a > *(int *)b) {
        return 1;   // a 大于 b
    } else {
        return 0;   // a 等于 b
    }
}

这里的关键点是,ab 是指向 void 类型的指针,我们需要把它们转换成指向 int 的指针,以便能够访问它们所指向的整数值。

步骤 2: 调用 qsort 函数

接下来,我们可以在 main 函数中定义一个整数数组,并调用 qsort 函数对其进行排序。

#include <stdio.h>
#include <stdlib.h>

// 比较函数
int compare(const void *a, const void *b) {
    if (*(int *)a < *(int *)b) {
        return -1;  // a 小于 b
    } else if (*(int *)a > *(int *)b) {
        return 1;   // a 大于 b
    } else {
        return 0;   // a 等于 b
    }
}

int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);  // 计算数组元素个数

    // 调用 qsort 函数
    qsort(arr, n, sizeof(int), compare);

    // 输出排序后的数组
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

5.进阶 

// 定义比较函数
int compare(const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}

比较函数通常写成return (*(int *)a - *(int *)b);这种方法适用于整数数组的排序,但如果你需要对其他数据类型进行排序或者需要更复杂的比较逻辑,就需要修改这个函数以适应你的需求。

5.1 解释:

这个函数实现了以下功能:

1. 参数:
  • const void *a: 指向第一个要比较的元素的指针。
  • const void *b: 指向第二个要比较的元素的指针。
2. 返回值:
  • 如果 *(int *)a 小于 *(int *)b,则返回一个负数。
  • 如果 *(int *)a 等于 *(int *)b,则返回 0。
  • 如果 *(int *)a 大于 *(int *)b,则返回一个正数。
3. 实现细节:
  • *(int *)a 和 *(int *)b 分别将 void 指针转换为 int 指针,并解引用这些指针以获取实际的整数值。
  • *(int *)a - *(int *)b 进行减法运算,得到的结果直接作为函数的返回值。

这种实现方式非常简洁,通过减法操作自动处理了所有三种情况:

  • 如果结果是负数,则 a 应该排在 b 前面。
  • 如果结果是 0,则 a 和 b 相等。
  • 如果结果是正数,则 a 应该排在 b 后面。

5.2 为什么自动排序?

1. 减法操作

当执行 *(int *)a - *(int *)b 时,实际上是在计算两个整数值之间的差。这个差可以是正数、负数或零,这取决于 ab 的相对大小。

  • 如果 *(int *)a < *(int *)b,那么差值将会是一个负数。
  • 如果 *(int *)a == *(int *)b,那么差值将会是零。
  • 如果 *(int *)a > *(int *)b,那么差值将会是一个正数。

2.qsort 排序机制

qsort 函数通过比较函数来确定元素的相对顺序。比较函数需要返回一个整数值来指示两个元素的相对位置关系。具体来说:

  • 如果比较函数返回一个负数,qsort 函数会认为 a 应该排在 b 的前面。
  • 如果比较函数返回零,qsort 函数会认为 a 和 b 的位置无关紧要,因为它们相等或不需要交换。
  • 如果比较函数返回一个正数,qsort 函数会认为 a 应该排在 b 的后面。

3. 实现细节

由于 qsort 函数内部的排序算法(通常是快速排序或其他高效算法)需要知道如何比较两个元素,所以它会调用提供的比较函数,并根据返回值决定如何排列元素。

三、模拟实现qsort函数

除了使用标准库中的qsort函数,我们还可以自己模拟实现一个类似的排序函数,比如使用冒泡排序算法。下面是一个简单的冒泡排序实现,它能够对不同的数据类型进行排序。

冒泡排序实现
#include <stdio.h>
#include <stdlib.h>

void Swap(char *buf1, char *buf2, int sz) {
    int i;
    for (i = 0; i < sz; i++) {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

void bubble_sort(void *base, size_t num, size_t sz, int (*compar)(const void *, const void *)) {
    size_t i, j;
    for (i = 0; i < num - 1; i++) {
        for (j = 0; j < num - 1 - i; j++) {
            if (compar((char *)base + j * sz, (char *)base + (j + 1) * sz) > 0) {
                Swap((char *)base + j * sz, (char *)base + (j + 1) * sz, sz);
            }
        }
    }
}

使用bubble_sort函数

使用bubble_sort函数与使用qsort函数类似,都需要提供相同的参数。这里不再重复给出示例代码。

四、总结

qsort函数是一个非常有用的工具,它提供了高度灵活的排序能力。通过自定义比较函数,我们可以轻松地对不同类型的数据进行排序。此外,通过上面的示例可以看出,即使使用简单的冒泡排序算法也能实现类似的功能,尽管在性能上可能不如qsort函数高效。

希望这篇文章对你理解和使用qsort函数有所帮助!

五、共勉

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

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

相关文章

java编程练习(初学者)每日练题

一、前言&#xff08;目前java的就业环境&#xff09; Java仍然是IT行业中最受欢迎和广泛使用的编程语言之一&#xff0c;特别是在企业级应用、后端服务、金融系统、大型网站、游戏开发等领域。Java岗位的需求依然强劲&#xff0c;体现在多个方面&#xff1a;1.企业级应用&…

Linux通过Docker安装Microsoft Office+RDP远程控制

之前写过一篇使用KVM虚拟机安装Microsoft OfficeRDP远程控制的文章&#xff0c;根据B站的教程安装后&#xff0c;发现有远程控制延迟的问题&#xff0c;比如拖动Office窗口时会延迟&#xff0c;搜狗输入法扫一下就闪退&#xff0c;插入形状后无法调整大小&#xff0c;无法调整图…

十七、【人工智能】【机器学习】【非监督学习】- K-均值 (K-Means)

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

热门超声波清洗机有哪些?值得入手的超声波清洗机品牌推荐

相信大家对超声波清洗机并不陌生&#xff0c;每次眼镜脏了&#xff0c;去眼镜店清洗时&#xff0c;店员使用的就是超声波清洗机。这种机器通过超声波的原理&#xff0c;能深入清洁物品内部&#xff0c;清洁效果非常好。相比于手动清洗&#xff0c;超声波清洗机可以在清洁过程中…

PHP健身微信小程序系统源码

&#x1f3cb;️‍♀️健身新潮流&#xff01;解锁“健身微信小程序”的全方位塑形秘籍 &#x1f4f1;开篇&#xff1a;掌中健身房&#xff0c;随时随地动起来 你还在为找不到合适的健身场地或教练而烦恼吗&#xff1f;是时候告别这些束缚&#xff0c;拥抱“健身微信小程序”…

身在职场,要认清的3个真理,让你把同事远远甩在身后

总有人被一些表面的现象所以蒙蔽&#xff0c;还沾沾自喜以为自己掌握了“真理”。职场上从来不缺“好心人”&#xff0c;总是用所谓的经验来告诫别人&#xff0c;应该如何做事。 大家都在说的事情&#xff0c;就一定是对的&#xff1f;那么为什么大多数人还活不成自己想成为的…

基于“日志审计应用”的 DNS 日志洞察实践

作者&#xff1a;羿莉 (萧羿) 基础背景 DNS(Domain Name System) [ 1] 是任何网络活动的基础。它将易于记忆的域名转换为机器能够理解的 IP 地址。监控 DNS 服务可以帮助用户识别网络活动并保持系统安全。出于合规和安全性的考虑&#xff0c;公司通常要求对网络日志进行存储和…

手撕算法题3 (附源码和思路)

算法 1.有效的括号2.用队列实现栈3.用栈实现队列3.设计循环队列 1.有效的括号 有效的括号 思路 借助栈这样的数据结构&#xff0c;将所有左括号进行入栈&#xff0c;所有右括号与出栈的括号比较&#xff0c;相同循环继续&#xff0c;不同直接返回false。循环结束后检查栈是否为…

模拟队列--C++

用数组来表示队列&#xff0c;怎么表示呢&#xff1f;我们先假设hh为头&#xff0c;tt为尾,当弹出队头的时候我们只需要把hh加一下就连可以了&#xff0c;相反tt一样也可以 #include<iostream> using namespace std; const int N1e510; int a[N],tt-1,hh0;//hh头 tt尾 i…

2.Linux_vi编辑器

打开/创建文件 1、打开/创建文件 指令&#xff1a;vi 文件名 若文件不存在&#xff0c;则新建一个文件&#xff1b;若文件存在&#xff0c;则打开这个文件。 2、打开文件时&#xff0c;指定光标的位置 指令&#xff1a;vi 文件名 行号 注意&#xff1a;""和行号…

吴恩达机器学习-C1W3L1-逻辑回归分类

在本实验中&#xff0c;您将对比回归和分类。 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from lab_utils_common import dlc, plot_data from plt_one_addpt_onclick import plt_one_addpt_onclick plt.style.use(./deeplearning.mplstyle)分类…

JDK 8 升级 17 及 springboot 2.x 升级 3.x 指南

JDK 8 升级 17 简介 从 JDK 8 升级到 JDK 17 的过程中&#xff0c;有几个主要的变化&#xff0c;特别是 Java Platform Module System (JPMS) 的引入&#xff0c;以及一些包路径的调整。以下是与 JDK 17 相关的一些重要变化&#xff1a; Java Platform Module System (JPMS) …

The First项目报告:解读Trading Bot黑马,交易狙击手Banana Gun

Meme币市场的特点是高度投机性和波动性&#xff0c;一个项目可能在短时间内实现巨大涨幅&#xff0c;为投资者带来巨额回报。然而&#xff0c;这种市场也充满了不确定性&#xff0c;许多项目可能只是短暂的炒作&#xff0c;缺乏实际价值或长期发展的潜力。因此&#xff0c;对于…

实验2-4-3 求奇数分之一序列前N项和

//实验2-4-3 求奇数分之一序列前N项和#include<stdio.h> #include<math.h> int main(){int n;scanf("%d",&n);//输入正整数N&#xff1b;double sum0;for (int i 1; i < n * 2; i 2)/* 在表达式 for (int i 1; i < n * 2; i 2) 中&#x…

设计模式-策略模式的完整代码示例及测试验证

策略模式 什么是策略模式&#xff1f; 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以互换。 策略模式使得算法可以在不影响客户端的情况下发生变化。策略模式主…

研0 冲刺算法竞赛 day26 P1803 凌乱的yyy / 线段覆盖

P1803 凌乱的yyy / 线段覆盖 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 考点&#xff1a;线段覆盖 思路&#xff1a;将整体结束时间进行排序&#xff0c;在从头开始遍历计数 代码&#xff1a; #include<iostream> #include <algorithm> using namespace …

从零开始学习机器学习,掌握AI未来的关键!

从零开始学习机器学习 1. 介绍1.1 人工智能&#xff08;AI&#xff09;概述1.2 机器学习在人工智能中的应用1.3 机器学习基础概念 2. 监督学习2.1 什么是监督学习2.2 回归分析2.3 分类问题2.4 模型评估和选择 3. 无监督学习3.1 什么是无监督学习3.2 聚类算法3.3 降维技术 4. 深…

Spring源码解析(27)之AOP的核心对象创建过程2

一、前言 我们在上一节中已经介绍了Advisor的创建过程&#xff0c;当时我们创建的logUtil这bean&#xff0c;他在 resolveBeforeInstantiation返回的是null&#xff0c;那么就会继续往下执行doCreateBean方法。 二、源码分析 protected Object doCreateBean(String beanName,…

永结无间Ⅸ--你不需要LLM Agent

人们将目光锁定在下一个闪亮的事物上。FOMO 是人性的一部分。这也适用于企业。就像数据科学成为每个企业分析功能的热潮一样&#xff0c;Agentic Architecture 是大多数 AI 雷达上的热门目标。 但您是否考虑过您是否真的需要它&#xff1f; 实际情况是&#xff0c;您不需要 A…

解答|一年期HTTPS证书如何获取?

自2023年年底以来&#xff0c;各大平台陆续下架了一年期免费HTTPS证书&#xff0c;目前市面上已经不再提供一年期的免费证书。付费正式版证书成为首选&#xff01;而DV证书由于其低廉的价格广受个人或者中小企业的青睐。 下面是DV类型证书&#xff08;13个月时长&#xff09;的…