一些自定义函数

server/2024/10/17 18:29:41/

目录

一.strcmp()函数

二.strstr()函数

三.memcpy函数

四.memmove函数

五.strncpy函数

六.strcat函数

七.atoi函数

八.strlen函数


一.strcmp()函数

strcmp 函数是用于比较两个字符串是否相等的函数它通过逐个字符比较两个字符串的 ASCII 值,来判断它们的相对大小。

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int my_strcmp(const char* str1, const char* str2) {assert(str1 && str2);while (*str1 == *str2) {if (*str1 == '\0') {return 0;}str1++;str2++;}if (*str1 > *str2)return 1;elsereturn -1;//或者//return *str1 - *str2;
}
int main() {char arr1[] = {"abcdef"};char arr[] = {"abc"};int ret = my_strcmp(arr1, arr);if (ret == 0) {printf("=");}else if (ret >= 1) {printf(">");}elseprintf("<");return 0;
}

代码解释:

  • 形参:用const修饰指针所指向的对象,表示通过该指针不能修改所指向的对象。

  • 断言 assert(str1 && str2): 该断言确保传入的两个字符串指针不为 NULL,即确保输入有效。如果任何一个指针为 NULL,程序会在调试模式下抛出异常。

  • while 循环while (*str1 == *str2) 表示当两个字符串当前字符相等时,继续循环进行比较。通过字符指针 str1str2 遍历每一个字符,直到遇到不相等的字符或者字符串的末尾(即 '\0')。

  • 判断结束条件: 当 *str1 == '\0' 时,表示字符串已经比较完毕,而且两者完全相同。因此,直接返回 0,表示两个字符串相等。

  • 返回比较结果

    • 如果 *str1 > *str2,则返回 1,表示 str1 在字典序上大于 str2
    • 如果 *str1 < *str2,则返回 -1,表示 str1 在字典序上小于 str2

二.strstr()函数

strstr 函数是 C 语言标准库中的一个字符串处理函数,用于查找一个字符串中是否包含另一个子字符串

const char* my_strstr(const char* str1, const char* str2) {assert(str1 && str2);const char* s1 = str1;const char* s2 = str2;const char* cur = str1;//cur指针记可能的开始匹配的位置if (*str2 == '\0') {//str2是空字符串直接返回str1return str1;}//外层 while 循环遍历 str1,直到到达字符串末尾。每次//循环都将 s1 指向 cur,将 s2 指向 str2。while (*cur) {//完成一次匹配s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2) {s1++;s2++;//内层 while 循环用于逐个字符比较 s1 //和 s2 指向的字符。如果它们相等,两个指针都向后移动。}if (*s2 == '\0') {return cur;//如果 s2 到达字符串末尾,说明 str2 完//全匹配了 str1 中以 cur 开始的位置,返回 cur。}cur++;//如果没有找到匹配,移动 cur 指针,继续检查下一个可能的匹配位置。}
}
int main() {char arr1[] = { "abcdefhij" };char arr2[] = { "def" };const char* ret = my_strstr(arr1, arr2);if (ret == NULL) {printf("找不到\n");}elseprintf("找到了:%s", ret);return 0;
}

代码解释

(代码注释)

三.memcpy函数

memcpy 函数是 C 标准库中的一个函数,用于在内存中复制一块数据。作用是将源内存区域的数据复制到目标内存区域

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
//void* arr1: 指向目标内存的指针。
//const void* arr2: 指向源内存的指针。
//size_t num: 要复制的字节数。
void my_memcpy(void* arr1, const void* arr2, size_t num) {//保存 arr1 的初始地址,以便在函数结束时返回(虽然在此代码中未实际返回)。void* ret = arr1;assert(arr1 && arr2);//使用 char* 类型强制转换,使得可以逐字节复制数据。//每次循环将 arr2 当前指针所指向的字节值复制到 arr1,然后两个指针都向前移动一个字节。while (num--) {*(char*)arr1 = *(char*)arr2;arr1=(char*)arr1+1;arr2=(char*)arr2+1;}//可省略,只是为了还原原函数的返回值return ret;
}
int main() {int arr[] = { 1,2,2,4 };int arr1[2] = { 0 };//注意这里的自定义memcpy不能够复制自身,但是在这个编译器的标准库函数memcpy函数可以,但是其他编译器不一定可以哦my_memcpy(arr1,arr,2*sizeof(int));int sz = sizeof(arr1) / sizeof(arr1[0]);for (int i = 0; i < sz; i++) {printf("%d ", arr1[i]);}return 0;
}

代码解释:

这个函数在遇到 '\0' 的时候并不会停下来

如果source和destination有任何的重叠,复制的结果都是未定义的

函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置

(代码注释)

四.memmove函数

memmove 是 C 标准库中的一个函数,用于在内存区域之间复制数据。它与 memcpy 类似,但 memmove 能够安全地处理源和目标内存区域重叠的情况(比如以下代码的例子),而 memcpy 不能。

#include<stdio.h>
#include<string.h>void* my_memmove(void* dest, const void* src, size_t num) {// 存储目标地址,方便后续返回void* ret = dest;if (dest < src) {// 前 -> 后复制while (num--) {*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}} else {// 后 -> 前复制while (num--) {*((char*)dest + num) = *((char*)src + num);}}return ret;
}int main() {int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };my_memmove(arr + 2, arr, sizeof(int) * 5);for (int i = 0; i < 10; i++) {printf("%d ", arr[i]);}return 0;
}

代码解释:

  • 返回值:标准库的 memmove 函数返回 dest 的地址,因此我们也应该返回 dest
  • 前向复制:当 dest 地址小于 src,需要从前往后复制,以避免覆盖源数据。
  • 后向复制:当 dest 地址大于或等于 src,为了避免重叠问题,我们从后往前复制数据。

五.strncpy函数

strncpy 是 C 标准库中的一个字符串处理函数,定义在 <string.h> 头文件中。它用于将一个字符串的指定长度部分复制到另一个字符串中。

#include <stdio.h>
//char* dest:目标字符串指针,用于存储复制后的字符串。
//const char* src:源字符串指针,要复制的字符串。
//size_t n:要复制的最大字符数。
char* my_strncpy(char* dest, const char* src, size_t n) {size_t i;for (i = 0; i < n && src[i] != '\0'; i++) {dest[i] = src[i];}// 如果 src 的长度小于 n,填充剩余部分为 '\0'for (; i < n; i++) {dest[i] = '\0';}return dest;
}int main() {char dest[10];my_strncpy(dest, "Hello", 10);printf("%s\n", dest);  // 输出: Helloreturn 0;
}

代码解释:

  • 第一个 for 循环:将 src 的字符逐个复制到 dest 中,直到 n 个字符或遇到源字符串的末尾('\0')。
  • 第二个 for 循环:如果 src 的长度小于 n,则在 dest 的剩余部分填充 '\0'

六.strcat函数

strcat 函数是 C 标准库中的一个字符串处理函数,功能是将源字符串 src 追加到目标字符串 dest 的末尾。目标字符串 dest 必须有足够的空间来容纳源字符串 src 和终止字符 \0

#include <stdio.h>
//dest 是目标字符串,字符将追加到它的末尾。
//src 是源字符串,来自它的前 n 个字符会被复制。
//n 是指定要复制的字符数。
char* my_strncat(char* dest, const char* src, size_t n) {char* original_dest = dest;// 移动到 dest 字符串的末尾while (*dest) {dest++;}// 复制 src 的前 n 个字符到 destwhile (n-- && *src) {*dest++ = *src++;}// 添加终止符*dest = '\0';//返回起始地址return original_dest;
}int main() {char dest[20] = "Hello";my_strncat(dest, " World", 3);printf("%s\n", dest);  // 输出: Hello Woreturn 0;
}

代码解释

  • 移动 dest 指针

    • while (*dest) 这部分将 dest 指针移动到目标字符串的末尾,因为字符串以 \0 结尾,所以遇到 \0 时停止。
  • 复制 src 字符串的前 n 个字符

    • 通过 while (n-- && *src),逐个字符复制源字符串的前 n 个字符,直到 n 变为 0 或者源字符串的末尾 \0
  • 添加终止符

    • 最后添加一个 \0 来终止目标字符串。
  • 返回值

    • 返回原始的 dest 地址,因为 dest 在过程中被修改(移动到了字符串末尾),所以要保留原始的起始地址。

七.atoi函数

atoi(ASCII to Integer)函数是标准 C 库函数,用于将字符串转换为整数。它从字符串的开头提取整数值,忽略前导空格,直到遇到一个非数字字符为止。函数声明在 <stdlib.h> 头文件中。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <limits.h> // 用于 INT_MAX 和 INT_MINint my_atoi(const char* str) {int sign = 1;  // 标志符号,默认正数long result = 0;  // 存储结果,用 long 来处理可能的溢出int i = 0;// 1. 跳过字符串前面的空白字符while (str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r' || str[i] == '\v' || str[i] == '\f') {i++;}// 2. 处理正负号if (str[i] == '-' || str[i] == '+') {if (str[i] == '-') {sign = -1;  // 如果是负号,sign = -1}i++;}// 3. 处理数字字符while (str[i] >= '0' && str[i] <= '9') {result = result * 10 + (str[i] - '0');  // 将字符转换为整数并累加到结果// 4. 处理溢出问题if (result > INT_MAX) {if (sign == 1) {return INT_MAX;  // 正溢出}else {return INT_MIN;  // 负溢出}}i++;}return (int)(result * sign);  // 返回转换后的整数,带符号
}int main() {char str1[] = "  -12345";char str2[] = "4193 with words";char str3[] = "words and 987";char str4[] = "-91283472332";printf("String: '%s' -> Integer: %d\n", str1, my_atoi(str1));  // 输出 -12345printf("String: '%s' -> Integer: %d\n", str2, my_atoi(str2));  // 输出 4193printf("String: '%s' -> Integer: %d\n", str3, my_atoi(str3));  // 输出 0printf("String: '%s' -> Integer: %d\n", str4, my_atoi(str4));  // 输出 INT_MIN (-2147483648)return 0;
}

代码解释:

  • 跳过空白字符

    • 使用 while 循环跳过空白字符(空格、换行、制表符等)。这些字符在 C 语言中有多种表示形式,包括 ' ', '\t', '\n', '\r', '\v', '\f'
  • 处理正负号

    • 如果遇到正负号('+''-'),相应地设置 sign 的值为 1 或 -1。
    • 默认情况下,sign 的初值为 1(正数)。
  • 处理数字字符

    • 将连续的数字字符逐个转换为整数,并逐步累加到 result 变量中。每次新数字 str[i] 加入结果时,执行 result = result * 10 + (str[i] - '0'),其中 (str[i] - '0') 是将字符数字转换为对应的整数。
  • 处理溢出

    • 如果 result 大于 INT_MAX,根据 sign 值判断是正溢出还是负溢出,并返回 INT_MAXINT_MIN
    • 使用 long 类型的 result 来确保不会在计算过程中发生溢出。
  • 返回结果

    • 最后,返回带符号的整数值 result * sign

ps:

  • 溢出处理:如果数字超出 int 类型的范围,即大于 INT_MAX (2147483647) 或小于 INT_MIN (-2147483648),返回相应的最大或最小值。
  • 非数字字符处理:当遇到第一个非数字字符时,停止转换并返回当前已转换的整数。
  • 空字符串处理:在本例中,没有特殊处理空字符串。如果输入字符串为空或全是非数字字符,返回值为 0

八.strlen函数

strlen 函数是 C 语言中的标准库函数,用于计算并返回一个以空字符 '\0' 结尾的字符串的长度。该长度不包括字符串的终止符 '\0',仅包含字符串中的实际字符数。

#include<stdio.h>
#include<assert.h>
#include<string.h>
//注意arr不能写为arr[],因为你要传进来数组的首元素
size_t my_strlen(const char* arr) {//方法一size_t count = 0;assert(arr != NULL);//注意解引用while (*arr) {count++;arr++;}return count;//方法二(指针-指针等于元素的个数)/*char* start = arr;assert(arr != NULL);while (*arr) {arr++;}return arr - start;*///方法三(递归)//记得解引用/*if (*arr == '\0') {return 0;}else {return my_strlen(arr + 1) + 1;}*/}
int main() {char arr[] = { "abcdef" };size_t len =my_strlen(arr);printf("%zd", len);return 0;
}

代码解释:

  • 方法一(迭代法)

    • 使用 while 循环遍历字符串的每个字符,直到遇到字符串的结束符 '\0'
    • 每次循环增加 count 计数器,最后返回 count
  • 方法二(指针运算法)

    • 首先保存原始指针 start,然后移动指针 arr 直到 '\0'
    • 返回 arrstart 之间的差值,即为字符串的长度。
  • 方法三(递归法)

    • 使用递归调用自身,每次传入下一个字符的指针。如果遇到 '\0' 返回 0,否则返回 my_strlen(arr + 1) + 1


http://www.ppmy.cn/server/131420.html

相关文章

(PyTorch) 深度学习框架-介绍篇

前言 在当今科技飞速发展的时代&#xff0c;人工智能尤其是深度学习领域正以惊人的速度改变着我们的世界。从图像识别、语音处理到自然语言处理&#xff0c;深度学习技术在各个领域都取得了显著的成就&#xff0c;为解决复杂的现实问题提供了强大的工具和方法。 PyTorch 是一个…

【记录】PPT|PPT 箭头相交怎么跨过

众所周知&#xff0c;在PPT中实现“跨线”效果并非直接可行&#xff0c;这一功能仅存在于Visio中。然而&#xff0c;通过一些巧妙的方法&#xff0c;我们可以在PPT中模拟出类似的效果。怎么在PPT中画交叉但不重叠的线-百度经验中介绍了一种方法&#xff0c;而本文将介绍一种改进…

带你了解linux:学习第十六课 linux 之 Ftp Sftp

今天要介绍的是最常用的传输工具 Ftp和Sftp。 FTP&#xff08;File Transfer Protocol&#xff09;和SFTP&#xff08;Secure File Transfer Protocol&#xff09;是两种用于在网络上进行文件传输的协议。FTP是较早的协议&#xff0c;而SFTP是基于SSH&#xff08;Secure Shell…

通信工程学习:什么是SRAM静态随机存取存储器

SRAM&#xff1a;静态随机存取存储器 SRAM&#xff0c;全称为Static Random-Access Memory&#xff0c;即静态随机存取存储器&#xff0c;是一种重要的随机存取存储器类型。以下是对SRAM的详细介绍&#xff1a; 一、定义与特点 定义&#xff1a; SRAM是一种只要保持通电&#…

ChatGPT:引领人工智能新潮流!

一、ChatGPT 是什么&#xff1f; 1. ChatGPT 的强大功能和广泛应用。 ChatGPT 作为一款先进的 AI 语言模型&#xff0c;拥有众多强大功能。它可以进行文本生成、文本分类、情感分析、机器翻译等多种自然语言处理任务。同时&#xff0c;ChatGPT 还能进行对话式交互&#xff0c;…

Redis 缓存淘汰策略:LRU 和 LFU 的缺点及解决方案详解

引言 Redis 是一款高性能的内存数据库&#xff0c;它的缓存淘汰机制是保障内存使用效率和应用性能的关键。为了在内存有限的情况下保证缓存数据的有效性&#xff0c;Redis 提供了多种缓存淘汰策略&#xff0c;其中 LRU&#xff08;Least Recently Used&#xff0c;最近最少使用…

springmvc的处理流程

用户把请求发到前端控制器&#xff0c;前端控制器通过handlerMapping找到controller&#xff0c;controller调用service&#xff0c;service调用dao&#xff0c;从数据库拿到要获取的数据&#xff0c;然后modelandview给前端控制器&#xff0c;前端控制器通过viewresolver解析视…

磁盘存储链式结构——B树与B+树

红黑树处理数据都是在内存中&#xff0c;考虑的都是内存中的运算时间复杂度。如果我们要操作的数据集非常大&#xff0c;大到内存已经没办法处理了该怎么办呢&#xff1f; 试想一下&#xff0c;为了要在一个拥有几十万个文件的磁盘中查找一个文本文件&#xff0c;设计的…