C语言核心库函数底层实现:qsort、strcpy、strcat与strcmp
本文详细讲解如何利用C语言底层语法手动实现常用的标准库函数。涵盖基于回调函数的通用冒泡排序(qsort),以及涉及指针安全、内存拷贝与字符串比较的strcpy、strcat和strcmp源码解析与注意事项。
通用排序算法原理
C语言标准库中的 qsort 是一个泛型排序函数,其核心在于利用 void* 指针支持任意数据类型,并通过回调函数解决不同数据类型的比较问题。若需在不依赖标准库的情况下模拟实现,通常结合冒泡排序或快速排序的思想。
回调函数是指通过函数指针调用的函数。将比较逻辑以函数指针的形式传递给排序主函数,由主函数在特定条件下触发调用,从而实现逻辑与解耦。
第1步:设计通用的交换与比较接口
- 目的:封装底层字节交换逻辑,并提供可定制的比较规则。
- 入口:
swap函数负责按字节移动内存块,cmp函数负责定义元素大小关系。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
// 交换两个大小为 size 字节的内存块
void swap(void* p1, void* p2, int n)
{
for (int i = 0; i < n; ++i)
{
char tmp = *((char*)p1 + i); // 强制转为 char* 逐字节操作
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
// 整数比较回调示例
int cmp(const void* elem1, const void* elem2)
{
return (*((int*)elem1) - *((int*)elem2));
}
第2步:构建冒泡排序骨架
- 参数:
base(数组首地址)、count(元素个数)、size(单个元素大小)、cmp(比较函数)。 - 动作:通过指针算术运算定位相邻元素
(char*)base + j * size,调用cmp判断是否需要调用swap。
void Bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
int i, j;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
// 比较相邻元素,大于则交换
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
第3步:验证执行结果
void PrintArray(int ar[], int n)
{
for (int i = 0; i < n; i++)
printf("%d ", ar[i]);
printf("\n");
}
int main()
{
int ar[10] = { 1, 3, 4, 6, 2, 7, 9, 8, 22, 11 };
int sz = sizeof(ar) / sizeof(ar[0]);
PrintArray(ar, sz); // 输出原始序列
Bubble(ar, sz, sizeof(ar[0]), cmp); // 执行排序
PrintArray(ar, sz); // 输出排序后序列
return 0;
}
字符串拷贝函数 strcpy 的实现
strcpy 用于将源字符串复制到目标缓冲区。其关键在于正确处理字符串结束符 '\0' 以及确保内存的安全性。
第1步:明确实现约束与前置条件
- 源字符串必须包含
'\0'终止符,且该字符会被一并拷贝到目标空间。 - 目标缓冲区的内存容量必须足够容纳源字符串的全部内容。
- 目标内存必须是可写的(非 const)。
第2步:参数校验与指针保护
- 动作:使用
assert校验传入指针不为 NULL;使用临时指针pDest保存原始目标地址,以便最终返回起始位置。
#include<assert.h>
char* my_strcpy(char* strDestination, const char* strSource)
{
assert(strDestination != NULL && strSource != NULL);
char* pDest = strDestination;
// 循环拷贝直到遇到源字符串结束符
while (*strSource != '\0')
{
*pDest++ = *strSource++; // 先赋值,后指针自增
}
// 补上字符串结束符
*pDest = '\0';
return strDestination;
}
第3步:测试运行状态
int main()
{
char str1[20] = "HelloABC";
const char* str2 = "Linux"; // 建议作为 const 保护源字符串
char* res = my_strcpy(str1, str2);
// 此时 str1 内容变为 "Linux"
printf("Result: %s\n", res);
return 0;
}
字符串追加函数 strcat 的实现
strcat 用于将源字符串拼接到目标字符串末尾。实现时需先找到目标串的尾部,再进行覆盖拷贝。
第1步:寻找目标字符串末尾
- 目的:确定新内容的写入起点。
- 动作:遍历
strDestination直到*pDest == '\0'。
第2步:连续拷贝逻辑
char* my_strcat(char* strDestination, const char* strSource)
{
assert(strDestination != NULL && strSource != NULL);
char* pDest = strDestination;
// 1. 移动指针至目标串尾部
while (*pDest != '\0')
pDest++;
// 2. 将源串内容逐个拷贝至目标串后
while (*strSource != '\0')
*pDest++ = *strSource++;
// 3. 添加新的结束符
*pDest = '\0';
return strDestination;
}
第3步:注意事项
务必保证 strDestination 有足够的剩余空间。如果空间不足会导致缓冲区溢出(Buffer Overflow)。测试时,如 str1[20] 初始化为 "Helloabc",追加 "Linux" 后应变为 "HelloabcLinux"。
字符串比较函数 strcmp 的实现
strcmp 按字典序逐字符比较两个字符串,根据 ASCII 码差值返回整数。
第1步:比较规则定义
- 返回值 > 0:首个字符串大于第二个字符串。
- 返回值 = 0:两个字符串相等。
- 返回值 < 0:首个字符串小于第二个字符串。
第2步:指针同步递增比较
int my_strcmp(const char* string1, const char* string2)
{
assert(string1 != NULL && string2 != NULL);
int res = 0;
// 同时遍历,只要字符相同或都为空即继续
while (*string1 != '\0' || *string2 != '\0')
{
res = *string1 - *string2; // 计算 ASCII 差值
if (res != 0)
break; // 发现差异,跳出循环
string1++;
string2++;
}
// 规范化返回值为 1, 0 或 -1
if (res > 0)
res = 1;
else if (res < 0)
res = -1;
return res;
}
第3步:验证案例
int main()
{
const char* str1 = "Helloab";
const char* str2 = "HelloABCab";
int res = my_strcmp(str1, str2);
// 因为 'b' > 'B' (ASCII), 结果为正数,处理后为 1
printf("Comparison Result: %d\n", res);
return 0;
}
以上就是C语言模拟实现qsort、strcpy、strcat和strcmp的详细内容,更多关于C语言基础函数原理的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













