一些自定义函数

news2024/11/25 2:28:50

目录

一.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;
	else
		return -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(">");
	}
	else
		printf("<");
	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是空字符串直接返回str1
		return 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");
	}
	else
		printf("找到了:%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);  // 输出: Hello
    return 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 个字符到 dest
    while (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 Wo
    return 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_MIN

int 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));  // 输出 -12345
    printf("String: '%s' -> Integer: %d\n", str2, my_atoi(str2));  // 输出 4193
    printf("String: '%s' -> Integer: %d\n", str3, my_atoi(str3));  // 输出 0
    printf("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.coloradmin.cn/o/2211666.html

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

相关文章

QD1-P3 HTML 基础内容介绍

本节学习&#xff1a;HTML基础语法介绍。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p3 ‍ 一、运行HTML代码 在 HBuilderX编辑器中创建空项目&#xff0c;添加一个 html 文件 <!-- QD1-P3 HTML基础语法 --><!DOCTYPE html> <html><head>&l…

Java面试宝典-并发编程学习01

Java 并发编程学习 1、创建线程的有哪些方式&#xff1f; 创建线程的方式有以下几种&#xff1a; 1. 继承Thread类&#xff1a;创建一个类继承Thread类&#xff0c;并重写run()方法&#xff0c;然后通过创建类的实例来创建线程。 2. 实现Runnable接口&#xff1a;创建一个类实…

PH47框架下BBDB飞控基础开发平台极简教程

1 硬件准备 1.1 一块WeAct 的Stm32F411核心板 1.2 2个USB-TTL模块。 1.3 Stm32开发所必须的如STlink烧写器。 1.4 必要的线材。 2 软件准备 2.1 Stm32开发所必备的Keil开发环境。 2.2 PH47框架代码&#xff0c;下载链接 2.3 CSS及BBDB 控制站工程&#xff0c;下载链接 2.4…

鸿蒙面试的一些可能问到的点

页面跳转 router 鸿蒙中跳转主要有两种&#xff0c;一种是router&#xff0c;一种是Navigation&#xff0c;官方推荐使用Navigation。 Router适用于模块间与模块内页面切换&#xff0c;通过每个页面的url实现模块间解耦。模块内页面跳转时&#xff0c;为了实现更好的转场动效…

7.2-I2C的DMA中断

I2C的DMA中断 请先阅读完I2C的普通中断模式以后再阅读本教程 i2c的DMA模式 1.添加通道 &#xff0c;添加后的参数保持默认 2.可以看到自动给我们DMA添加了中断向量。 保存后只需要将下面_ IT改为_ DMA即可 运行代码 i2c1) { aht20State 4; } } /* USER CODE END 0 */ 以上就…

ssm基于java的网上手机销售系统

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 目 录 III 1 绪论 1 1.1 研究背景 1 1.2 目的和意义 1 1.3 论文结构安排 2 2 相关技术 3 2.1 SSM框…

yolov5环境GPU搭建 ,用GPU跑polov5算法

win10NVIDIA GeForce RTX 3050torch1.13.1torchaudio0.13.1torchvision 0.14.1 cuda11.7python3.8cudnn8.7.0 在环境搭建中踩了许多坑&#xff0c;yolov5环境的搭建需要依赖很多环境&#xff0c;用cpu跑很容易跑单张识别&#xff0c;用GPU跑却踩了很多坑&#xff0c;不过GPU环…

Mac 备忘录妙用

之前使用 Windows 的过程中&#xff0c;最痛苦的事是没有一款可以满足我快速进行记录的应用 基本都得先打开该笔记软件&#xff0c;然后创建新笔记&#xff0c;最后才能输入&#xff0c;这么多步骤太麻烦了 在切换到 MacOS 之后&#xff0c;让我惊喜的就是自带的备忘录&#…

【java面经thinking】一

目录 类加载过程 加载&#xff1a; 连接 初始化 GC回收机制&#xff08;垃圾回收&#xff09; 区域 判断对象是否存活 回收机制 HashMap 类加载器 加载标识 加载机制 缓存 自定义加载器&#xff1a; JVM内存结构 常量池 string设置成final 按下网址发生 类加…

C语言有关结构体的知识(后有通讯录的实现)

一、结构体的声明 1.1 结构体的定义 结构体是一些值的集合&#xff0c;这些值被称为成员变量。结构的每个成员可以是不同的类型 1.2 结构体的声明 这里以描述一个学生为例&#xff1a; struct stu {char name[10];//名字int age;//年龄char id[20];//学号char sex[5];//性别 }…

TIM定时器(标准库)

目录 一. 前言 二. 定时器的框图 三. 定时中断的基本结构 四. TIM定时器相关代码 五. 最终现象展示 一. 前言 什么是定时器&#xff1f; 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断。 TIM定时器不仅具备基本的定时中断功能&#xff0c;而且…

【LeetCode】708. 循环有序列表的插入

目录 一、题目二、解法完整代码 一、题目 给定循环单调非递减列表中的一个点&#xff0c;写一个函数向这个列表中插入一个新元素 insertVal &#xff0c;使这个列表仍然是循环非降序的。 给定的可以是这个列表中任意一个顶点的指针&#xff0c;并不一定是这个列表中最小元素的…

2024免费mac苹果电脑清理垃圾软件CleanMyMac X4.15.8

对于苹果电脑用户来说&#xff0c;设备上积累的垃圾文件可能会导致存储空间变得紧张&#xff0c;影响电脑的性能和使用体验。尤其是那些经常下载和安装新应用、编辑视频或处理大量照片的用户&#xff0c;更容易感受到存储空间的压力。面对这种情况&#xff0c;寻找一种有效的苹…

springboot3使用Excel导入数据库数据

一、导入依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.3.0</version></dependency> 二、…

Xilinx远程固件升级(二)——STARTUPE2原语的使用

通过&#xff08;一&#xff09;可以看出&#xff0c;对于远程固件升级实际上是通过调用flash不同区域的bit实现&#xff0c;通过golden image和update image共同保障了系统的稳定性。在项目中如果将flash的时钟直接绑定FPGA后进行约束&#xff0c;在综合编译时是无法通过的。这…

Spark:DataFrame介绍及使用

1. DataFrame详解 DataFrame是基于RDD进行封装的结构化数据类型&#xff0c;增加了schema元数据&#xff0c;最终DataFrame类型在计算时&#xff0c;还是转为rdd计算。DataFrame的结构化数据有Row&#xff08;行数据&#xff09;和schema元数据构成。 Row 类型 表示一行数据 …

MySQL 8.4修改初始化后的默认密码

MySQL 8.4修改初始化后的默认密码 &#xff08;1&#xff09;初始化mysql&#xff1a; mysqld --initialize --console &#xff08;2&#xff09;之后,mysql会生成一个默认复杂的密码&#xff0c;如果打算修改这个密码&#xff0c;可以先用旧密码登录&#xff1a; mysql -u…

Redis set类型 zset类型

set类型 类型介绍 集合类型也是保存多个字符串类型的元素的&#xff0c;但和列表类型不同的是&#xff0c;集合中 1&#xff09;元素之间是⽆序 的 2&#xff09;元素不允许重复 ⼀个集合中最多可以存储 个元素。Redis 除了⽀持 集合内的增删查改操作&#xff0c;同时还⽀持多…

【图书推荐】《R语言医学数据分析实践》

本书重点 梅俏、卢龙、丁健、张晟、黄龙、胡志坚、张琼瑶、林志刚等业内专家联袂推荐。 以公共医学数据为例&#xff0c;精选大量的实用案例&#xff0c;深入浅出地介绍统计建模分析方法。 帮助读者解决医学数据分析中遇到的实际问题。 通过实际操作引导读者入门科研论文数…

生信分析流程:从数据准备到结果解释的完整指南

介绍 生物信息学&#xff08;生信&#xff09;分析是一个复杂的过程&#xff0c;涉及从数据准备到结果解释的多个步骤。随着高通量测序技术的发展和生物数据的迅猛增长&#xff0c;了解和掌握生信分析的标准流程变得尤为重要。这不仅有助于提高分析的准确性&#xff0c;还能优…