一些自定义函数

时间:2024-10-14 07:29:52

目录

一.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