当前位置: 首页 > article >正文

【C语言基础】双指针在qsort函数中的应用

在C语言中使用 qsort 对字符串数组(如 char* 数组)排序时,必须转换为双指针(char**,这是由字符串数组的内存结构和 qsort 的工作原理决定的。以下是详细解释:


一、底层原理分析

1. 字符串数组的内存结构

假设有一个字符串数组:

char* strs[] = {"apple", "banana", "cherry"};

其内存布局如下:

strs[0] → 指向 "apple" 的首地址
strs[1] → 指向 "banana" 的首地址
strs[2] → 指向 "cherry" 的首地址

每个元素 strs[i] 的类型是 char*(字符串指针),因此:

  • 数组名 strs 的类型是 char**(指向指针的指针)
  • qsort 传递给比较函数的是 strs[i] 的地址(即 &strs[i]),其类型为 char**

2. qsort 的比较函数参数

qsort 的比较函数原型为:

int compar(const void *a, const void *b);
  • 参数 ab 是数组元素的指针,即 &strs[i]&strs[j]
  • 对于字符串数组,ab 的类型是 char**(指针的指针)。

二、正确转换步骤

1. 错误写法(直接转 char*
// 错误!会导致比较的是字符串内容,而非指针地址
int compare_strings_wrong(const void *a, const void *b) {const char *str1 = (const char*)a; // ❌ 直接转换const char *str2 = (const char*)b;return strcmp(str1, str2);
}
  • 问题ab 本质是 &strs[i]char** 类型),若直接转为 char*,实际比较的是指针地址值,而非字符串内容。

2. 正确写法(转双指针后解引用)
int compare_strings(const void *a, const void *b) {const char **str1 = (const char **)a; // ✅ 转换为双指针const char **str2 = (const char **)b;return strcmp(*str1, *str2); // 解引用得到实际的字符串指针
}
  • 关键点
    • a/b 转为 char**(因为它们本质是 char** 类型)
    • 解引用(*str1)得到实际的 char*(字符串首地址)
    • 比较字符串内容而非指针地址。

三、类比其他类型

int 数组为例:

int arr[] = {5, 2, 8};
  • 比较函数的参数是 int*(数组元素地址),直接解引用即可:
int compare_ints(const void *a, const void *b) {int num1 = *(const int*)a; // ✅ 直接转 int*int num2 = *(const int*)b;return num1 - num2;
}
  • 字符串数组与之不同,因为数组元素本身是指针(char*),所以需要多一层解引用。

四、常见错误场景

1. 错误结果示例

若错误地直接转换:

const char *str1 = (const char*)a; // a 是 char**,转 char* 后值等于 &strs[i]
const char *str2 = (const char*)b;
  • str1 的值是 &strs[i](即 char** 的地址值)
  • strcmp(str1, str2) 会比较这两个地址值的 ASCII 码,而非字符串内容,导致排序混乱。

2. 内存访问崩溃

若字符串数组元素为 NULL

char* strs[] = {"apple", NULL, "cherry"};
  • 错误转换后,strcmp 会尝试访问 NULL 地址,导致段错误。

五、总结

数据类型qsort 参数类型比较函数转换方式
int[]int*(指向元素的指针)*(const int*)a
char*[]char**(指向指针的指针)*(const char**)a

核心原则qsort 始终传递数组元素的地址,需根据元素类型决定如何转换和解引用。

以下是引用

逐步解释

  1. 定义比较函数

    int compare_strings(const void *a, const void *b) {const char **str1 = (const char **)a;const char **str2 = (const char **)b;return strcmp(*str1, *str2);
    }
    
    • const void *aconst void *bqsort 传递给比较函数的参数。
    • const char **str1const char **str2 是将 const void* 转换为 const char** 后的结果。
    • *str1*str2 分别是 str1str2 指向的字符串。
  2. 调用 qsort

    qsort(strs, len, sizeof(char*), compare_strings);
    
    • strs 是要排序的数组。
    • len 是数组的长度。
    • sizeof(char*) 是每个元素的大小(即每个元素是指针)。
    • compare_strings 是比较函数。
  3. 输出排序后的结果

    for (size_t i = 0; i < len; i++) {printf("%s ", strs[i]);
    }
    printf("\n");
    

为什么需要双指针转换

  • 第一次转换:将 const void* 转换为 const char**,因为 qsort 传递的是指向数组元素的指针。
  • 第二次转换:通过 *str1*str2 获取实际的字符串指针,以便使用 strcmp 函数进行比较。

通过这种方式,我们可以正确地对字符串数组进行排序,而不会出现类型不匹配的问题。

相关文章:

【C语言基础】双指针在qsort函数中的应用

在C语言中使用 qsort 对字符串数组&#xff08;如 char* 数组&#xff09;排序时&#xff0c;必须转换为双指针&#xff08;char**&#xff09;&#xff0c;这是由字符串数组的内存结构和 qsort 的工作原理决定的。以下是详细解释&#xff1a; 一、底层原理分析 1. 字符串数组…...

Android组件刷新

Android中刷新View的方法有以下几种&#xff1a; 调用invalidate()方法&#xff0c;该方法会使View树中的所有视图无效或脏&#xff0c;等待下一次绘制时重新绘制。例如&#xff1a; mCustomView.invalidate(); 调用postInvalidate()方法&#xff0c;该方法类似于invalidate()…...

JavaWeb开发 Servlet底层 Servlet 过滤器 过滤器和拦截器 手写一个限制访问路径的拦截器

目录 万能图 过滤器自我理解 案例 实现Filter 接口 配置文件 web.xml 将过滤器映射到 servlet 用处 拦截器 手写案例 重写 preHandle() 方法 拦截处理 重写 postHandle() 方法 后处理 重写 afterHandle() 方法 完成处理 代码 如何配置拦截器 万能图 还是看一下这张…...

QT中多线程写法

转自个人博客&#xff1a;QT中多线程写法 1. QThread及moveToThread() 使用情况&#xff1a; 多使用于需要将有着复杂逻辑或需要一直占用并运行的类放入子线程中执行的情况&#xff0c;moveToThread是将整个类的对象移入子线程。 优缺点&#xff1a; 优点&#xff1a;更符合…...

0415-批量删除操作

关于删除的全部代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>Insert title here</title> <script src"js/jquery-3.7.1.min.js"></script> <srcipt src"js/jqu…...

Day08 【基于余弦相似度实现的表示型文本匹配】

基于余弦相似度实现的表示型文本匹配 目标数据准备参数配置数据处理初始化方法加载数据编码句子补齐与截断重写父类两个方法采样策略加载词表和方案 模型构建SentenceEncoder类SiameseNetwork类优化器配置 主程序验证与评估推理预测测试结果 目标 本文基于给定的词表&#xff…...

【leetcode hot 100 72】编辑距离

解法一&#xff1a;递归 解法二&#xff1a;&#xff08;动态规划&#xff09;①定义&#xff1a;dp[i][j]为word1中前i个字符转化为word2中前j个字符所需操作数;dp[m1][n1] ②初始状态&#xff1a;dp[0][j]j(0变为j&#xff0c;需要j步)&#xff0c;dp[i][0]i(i变为0&#xff…...

Dubbo、HTTP、RMI之间的区别

Dubbo、HTTP、RMI之间的区别如下&#xff1a; 表格 复制 特性DubboHTTPRMI通信机制基于Netty的NIO异步通信&#xff0c;采用长连接&#xff0c;支持多种序列化方式基于标准的HTTP协议&#xff0c;无状态&#xff0c;每次请求独立基于Java原生的RMI机制&#xff0c;支持Java对…...

Java练习——day1(反射)

文章目录 练习1练习2练习3思考封装原则与反射合理使用反射“破坏”封装的场景 练习1 编写代码&#xff0c;通过反射获取String类的所有公共方法名称&#xff0c;并按字母顺序打印。 示例代码&#xff1a; import java.lang.reflect.Method; import java.util.Arrays;public …...

golang的slice扩容过程

Go 语言中的切片扩容机制是 Go 运行时的一个关键部分&#xff0c;它确保切片在动态增加元素时能够高效地管理内存。这个机制是在 Go 运行时内部实现的&#xff0c;涉及了内存分配、数据拷贝和容量调整。扩容的实现主要体现在 runtime.growslice 函数中。下面我们将深入分析 Go …...

Swift —— delegate 设计模式

一、什么是 delegate 模式 所谓 delegate 就是代理模式。简单来说&#xff0c;delegate 模式就是在类的函数里运行完一段代码后&#xff0c;你可以通过一个符合某个代理协议的属性来调代理的方法。其中&#xff0c;代理方法就是回调函数。 二、delegate 模式与闭包比的优势 …...

Docker 安装 Elasticsearch 8.x

Docker 安装 Elasticsearch 8.x 前言一、准备工作二、设置容器的目录结构三、启动一个临时的容器来复制配置文件四、复制配置文件到本地目录五、删除临时容器六、创建并运行容器&#xff0c;挂载本地目录七、修改文件配置监听端口八、端口配置&#xff1a;Host 网络模式 vs Por…...

Vue工程化开发脚手架Vue CLI

开发Vue有两种方式 核心包传统开发模式&#xff1a;基于html / css / js 文件&#xff0c;直接引入核心包&#xff0c;开发 Vue。工程化开发模式&#xff1a;基于构建工具&#xff08;例如&#xff1a;webpack&#xff09;的环境中开发Vue。 脚手架Vue CLI Vue CLl 是 Vue 官方…...

开源智慧巡检——无人机油田AI视频监控的未来之力

油田巡检&#xff0c;关乎能源命脉&#xff0c;却常受困于广袤地形、高危环境和人工效率瓶颈。管道泄漏、设备故障、非法闯入——这些隐患稍有疏忽&#xff0c;便可能酿成大患。传统巡检已无法满足现代油田对安全与效率的需求&#xff0c;而无人机油田巡检系统正以智能化之力重…...

数字ic后端设计从入门到精通(含fusion compiler, tcl教学)

pin 在集成电路设计中&#xff0c;特别是在使用工具如 Fusion Compiler 时&#xff0c;理解“引脚”&#xff08;pin&#xff09;的基础知识对于设计、优化和验证电路至关重要。以下是从 Fusion Compiler 的角度出发&#xff0c;关于引脚&#xff08;pin&#xff09;的基础知识…...

Django从零搭建卖家中心登陆与注册实战

在电商系统开发中&#xff0c;卖家中心是一个重要的组成部分&#xff0c;而用户注册与登陆则是卖家中心的第一步。本文将详细介绍如何使用Django框架从零开始搭建一个功能完善的卖家注册页面&#xff0c;包括前端界面设计和后端逻辑实现。 一、项目概述 我们将创建一个名为sel…...

MySQL表的使用(4)

首先回顾一下之前所学的增删查改&#xff0c;这些覆盖了平时使用的80% 我们上节课中学习到了MySQL的约束 其中Primary key 是主键约束&#xff0c;我们今天要学习的是外键约束 插入一个表 外键约束 父表 子表 这条记录中classid为5时候&#xff0c;不能插入&#xff1b; 删除…...

ollama修改配置使用多GPU,使用EvalScope进行模型压力测试,查看使用负载均衡前后的性能区别

文章目录 省流结论机器配置不同量化模型占用显存1. 创建虚拟环境2. 创建测试jsonl文件3. 新建测试脚本3. 默认加载方式&#xff0c;单卡运行模型3.1 7b模型输出213 tok/s3.1 32b模型输出81 tok/s3.1 70b模型输出43tok/s 4. 使用负载均衡&#xff0c;多卡运行4.1 7b模型输出217t…...

深入定制 QSlider——实现精准点击跳转与拖拽区分

在使用 Qt 编写界面应用时,QSlider 是一个常用的滑动控件。但你可能会注意到,默认情况下点击滑轨(groove)区域时,滑块并不会直接跳到鼠标点击的位置,而是按照内部的分页步进(page step)行为响应。此外,垂直 Slider 在点击最底部时还存在 releaseEvent(或 sliderRelea…...

Dijkstra算法求解最短路径—— 从零开始的图论讲解(2)

前言 在本系列第一期:从零开始的图论讲解(1)——图的概念,图的存储,图的遍历与图的拓扑排序-CSDN博客 笔者给大家介绍了 图的概念,如何存图,如何简单遍历图,已经什么是图的拓扑排序 按照之前的学习规划&#xff0c;今天笔者将继续带大家深入了解图论中的一个核心问题&#x…...

Java Spring Cloud框架使用及常见问题

Spring Cloud作为基于Spring Boot的分布式微服务框架&#xff0c;显著简化了微服务架构的开发与管理。其核心优势包括集成Eureka、Ribbon、Hystrix等组件&#xff0c;提供一站式服务发现、负载均衡、熔断容错等解决方案&#xff0c;支持动态配置与消息总线&#xff0c;实现高效…...

[连载]Transformer架构详解

Transformer: Attention Is All You Need Paper 地址&#xff1a;https://arxiv.org/abs/1706.03762 Paper 代码&#xff1a;https://github.com/tensorflow/tensor2tensor Paper 作者&#xff1a;Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan…...

DeepSeek:穿透行业知识壁垒的搜索引擎攻防战

DeepSeek&#xff1a;穿透行业知识壁垒的搜索引擎攻防战 文 / 产业智能观察组&#xff08;人机协同创作&#xff09; 一、搜索引擎的"认知折叠"危机 2024年Q1数据显示&#xff0c;百度搜索结果前10页中&#xff0c;61.7%的内容存在"伪专业化"现象——看似…...

AF3 generate_chain_data_cache脚本解读

AlphaFold3 generate_chain_data_cache 脚本在源代码的scripts文件夹下。该脚本从指定目录中批量解析 mmCIF/PDB 文件的工具,并将每个链的基本信息(序列、分辨率、是否属于聚类等)提取并写入 JSON 文件,主要用于后续蛋白质建模、过滤或训练数据准备。 源代码: import ar…...

LVGL Video控件和Radiobtn控件详解

LVGL Video控件和Radiobtn控件详解 一、 Video控件详解1. 概述2. 创建和初始化3. 基本属性设置4. 视频控制5. 回调函数6. 高级功能7. 注意事项 二、Radiobtn控件详解1. 概述2. 创建和初始化3. 属性设置4. 状态控制5. 组管理6. 事件处理7. 样式设置8. 注意事项 三、效果展示四、…...

组合数哭唧唧

前言&#xff1a;手写一个简单的组合数&#xff0c;但是由于长期没写&#xff0c;导致一些细节没处理好 题目链接 #include<bits/stdc.h> using namespace std; #define endl "\n"#define int long longconst int N (int)2e510; const int Mod (int)1e97;int…...

NLP高频面试题(四十二)——RAG系统评估:方法、指标与实践指南

1. 引言:RAG系统概述与评估挑战 检索增强生成(Retrieval-Augmented Generation,简称 RAG)是近年来自然语言处理领域的一个重要进展。RAG系统在大型语言模型生成文本的过程中引入了外部检索模块,从外部知识库获取相关信息,以缓解纯生成模型可能出现的幻觉和知识盲点。通过…...

MySQL-COUNT函数使用

COUNT聚合函数 COUNT 是 MySQL 中一个常用的聚合函数&#xff0c;用于统计满足条件的行数。有3种使用方式&#xff1a; COUNT(*)&#xff1a;统计表中所有行的数量&#xff0c;包括 NULL 值。COUNT(字段)&#xff1a;统计指定字段非 NULL 值的行数。COUNT(条件查询)&#xff…...

Linux路漫漫

目录 Vim模式 基本操作 文本编辑 更多功能 1. 直接启动 Vim 2. 打开一个已存在的文件 3. 打开多个文件 4. 以只读模式打开文件 5. 从指定行号开始编辑 6. 快速打开并执行命令 7. 检查是否安装了 Vim 8. 退出 Vim 前提条件 SCP 命令格式 具体操作 1. Windows 命…...

游戏引擎学习第227天

今天的计划 今天的工作重点是进行吸引模式&#xff08;attract mode&#xff09;的开发&#xff0c;主要是处理游戏的进出和其他一些小的细节问题&#xff0c;这些是之前想要整理和清理的部分。我做了一些工作&#xff0c;将游戏代码中的不同部分分离到逻辑上独立的区域&#…...