目录
一.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)
表示当两个字符串当前字符相等时,继续循环进行比较。通过字符指针str1
和str2
遍历每一个字符,直到遇到不相等的字符或者字符串的末尾(即'\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_MAX
或INT_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'
。- 返回
arr
与start
之间的差值,即为字符串的长度。方法三(递归法):
- 使用递归调用自身,每次传入下一个字符的指针。如果遇到
'\0'
返回0
,否则返回my_strlen(arr + 1) + 1
。