本文主要介绍一些C语言中常用字符串函数及部分函数的模拟实现。以下函数均需要包含头文件:
目录
一、strlen函数
1.函数介绍
2.strlen函数的模拟实现(三种方法)
1.计数器
2.递归
3.指针相减
二、strcpy函数
1.函数介绍
2.strcpy函数的模拟实现
三、strcat函数
1.函数介绍
2.strcat函数的模拟实现
四、strcmp函数
1.函数介绍
2.strcmp函数的模拟实现
五、strncpy函数
1.函数介绍
六、strncat函数
1.函数介绍
七、strncmp函数
1.函数介绍
八、strstr函数
1.函数介绍
2.strstr函数的模拟实现
九、strtok函数
1.函数介绍
十、strerror函数
1.函数介绍
字符串以 '\0' 作为结束标志,strlen函数返回的实在字符串中 '\0' 前面出现的字符个数(不包含 '\0')。
注意点:
① 参数指向的字符串必须要以 '\0' 结束。
② 注意函数的返回值为size_t,是无符号的。(易错)
例题:下面代码的结果是?
#include
int i;
int main()
{
i--;
if (i > sizeof(i))
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
结果打印: >
解析:
C语言中,0为假,非0即为真。
全局变量,没有给初始值时,编译其会默认将其初始化为0。
i的初始值为0,i--结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形,因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,超过4或者8,因此结果输出大于号。
代码演示:
#include
#include
int main()
{
char arr[] = "abcd";
int len = strlen(arr);
printf("%d\n", len);
return 0;
}
输出结果:4
思路:
我们定义一个计数器count,当指针指向的内容不为 '\0' 时,count+1即可,最后返回count。
size_t my_strlen(const char* str)
{
size_t count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
思路:
当指针指向的内容不为 '\0' 时,1+该函数并且把指针加1作为参数。指针指向的内容为 '\0'时,返回0。
size_t my_strlen(const char* str)
{
if (*str)
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
思路:
两指针相减结果为两指针间的元素个数。所以只需找到指针该字符串 '\0' 的指针,与参数指针相减即可。
size_t my_strlen(const char* str)
{
char* p = str;
while (*p)
{
p++;
}
return p - str;
}
把指针source所指向的字符串复制到指针destination。
注意点:
① 源字符串必须以 '\0' 结束。
② 会将源字符串中的 '\0' 拷贝到目标空间。
③ 目标空间必须足够大,以确保能存放源字符串。
④ 目标空间必须可变。
代码演示:
#include
#include
int main()
{
char arr1[] = "abcdefgh";
char arr2[] = "***";
printf("%s\n", arr1);
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
拷贝前:abcdefgh
拷贝后:***
思路:
将src所指向的源字符串中的字符依次赋值到dest所指向的目标字符串,并返回目标字符串的起始地址。
char* my_strcpy(char* dest, const char* src)
{
char* p = dest;
while (*dest++ = *src++);
return p;
}
把source所指向的字符串追加到destination所指向的字符串的结尾。
注意点:
① 源字符串必须以 '\0' 结束。
② 目标空间必须有足够的大,能容纳下源字符串的内容。
③ 目标空间必须可修改。
④ 源字符串中的 '\0'会一并拷贝到目标空间,并删除源字符串中的 '\0'。
strcat函数为自己追加时,会将源字符串中的 '\0'覆盖。因此,strcat函数无法为自己追加。
代码演示:
#include
#include
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efg";
printf("%s\n", strcat(arr1, arr2));
return 0;
}
输出结果:abcdefg
思路:
先找到目标字符串 '\0'的位置,再在 '\0'处追加源字符串,并返回目标字符串的起始地址。
char* my_strcat(char* dest, char* src)
{
assert(dest, src);//确保两个字符串不为空指针
char* p = dest;
while (*p)
{
p++;
}
while (*p++ = *src++)
{
;
}
return dest;
}
把str1所指向的字符串和str2所指向的字符串进行比较。
该函数会比较两个字符串对应字符的ASCII值,直至两字符串都为 '\0'。
返回值:
代码演示:
#include
#include
int main()
{
char arr1[] = "abcd";
char arr2[] = "abfd";
printf("%d\n", strcmp(arr1, arr2));
return 0;
}
输入结果:-1 即字符串arr1 < arr2
思路:
比较两个字符串对应字符的ASCII值,若两字符相等,则比较下一对字符。若两字符串长度相等,则两个字符都为 '\0'时,两字符串相等。若两字符串长度不等,且前面部分都相等,则返回其中一个字符与 '\0'的差值。
int my_strcmp(const char* p1, const char* p2)
{
assert(p1 && p2);//确保两指针不为空指针
while (*p1 == *p2)
{
if (*p1 == '\0')
{
return 0;
}
p1++;
p2++;
}
return *p1 - *p2;
}
把source所指向的字符串复制到destination,最多复制num个字符。
注意点:
① 当source的长度小于 num 时,destination的剩余部分将用0填充。
其他部分与strcpy函数相同。
代码演示:
#include
#include
int main()
{
char arr1[10] = "abcdefg";
char arr2[] = "***";
printf("%s\n", strncpy(arr1, arr2, 4));
return 0;
}
输出结果:***
把source所指向的字符串追加到destination所指向的字符串的结尾,直到num字符长度为止。
注意点:
① 当源字符长度小于num时,只追加源字符串。
其他部分与strcat函数相同。
代码演示:
#include
#include
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "****";
printf("%s\n", strncat(arr1, arr2, 3));
return 0;
}
输出结果:abcdef***
把str1和str2进行比较,最多比较前num个字节。
注意点:
① 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
其他部分与strcmp函数相同。
代码演示:
#include
#include
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abchijkl";
printf("%d\n", strncmp(arr1, arr2,3));
}
输出结果:0 只比较前3个字符时相等。
在字符串str1中查找第一次出现字符串str2的位置,不包含终止符 '\0'。
返回值:
返回在字符串str1中中第一次出现str2字符串的位置,如果未找到则返回null。
代码演示:
#include
#include
int main()
{
char arr1[] = "abcdefgh";
char arr2[] = "cde";
printf("%s\n", strstr(arr1, arr2));
return 0;
}
输出结果:cdefgh
如果查找不到,则输出:(null)
思路:
将字符串str1与str2逐字符比较,并记录字符串str1当前字符的地址,如果不同,则str1+1再与str2逐字符比较,直到字符串str1当前字符为'\0'。
char* my_strstr(const char* s1, const char* s2)
{
assert(s1 && s2);//确保s1、s2不为空指针
char* cur = s1;
char* p1;
char* p2;
while (*cur)
{
p1 = cur;
p2 = s2;
while ( *p1 && *p2 && *p1 == *p2)
{
p1++;
p2++;
}
if (*p2 == '\0')
{
return cur;
}
cur++;
}
return NULL;
}
注意点:
① sep参数是个字符串,定义了用作分隔符的字符集合。
② 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
③ strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
④ strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
⑤ strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
⑥ 如果字符串中不存在更多的标记,则返回 NULL 指针。
代码演示:
#include
#include
int main()
{
char* p = "[email protected]";
const char* sep = "@.";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
str = strtok(arr, sep);
printf("%s\n", str);
str = strtok(NULL, sep);
printf("%s\n", str);
str = strtok(NULL, sep);
printf("%s\n", str);
return 0;
}
输出结果:
123456789
163
com
当分割段数增多时,代码则过于繁杂,所以用循环优化:
#include
#include
int main()
{
char* p = "[email protected]";
const char* sep = "@.";
char arr[30];
char* str = NULL;
strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
输出结果相同。
从内部数组中搜索错误号errnum,并返回一个指向错误信息字符串的指针。
代码演示:
#include
#include
#include//所需包含的头文件
int main()
{
printf("%s\n", strerror(errno));
return 0;
}
没有错误时:
输出结果:No error
#include
#include
#include//所需包含的头文件
int main()
{
FILE* pf;
pf = fopen("unexist.ent", "r");
if (pf == NULL)
printf("%s\n", strerror(errno));
return 0;
}
文件不存在时:
输出结果:No such file or directory(没有这样的文件或目录)