字符函数和字符串函数
- 前期背景
- 求字符串长度函数
- strlen函数
- strlen函数三种模拟实现
- 长度不受限制的字符串函数
- strcpy函数
- strcpy函数模拟实现
- strcat函数
- strcat函数模拟实现
- strcmp函数
- strcmp函数模拟实现
- 长度受限制的字符串函数
- strncpy函数
- strncpy函数模拟实现
- strncat函数
- strncat函数模拟实现
- strncmp函数
- strncmp函数模拟实现
- 字符串查找
- strstr函数
- strstr函数模拟实现
- strtok函数
- 错误信息报告
- strerror函数
- perror函数
- 字符分类函数
- 大小写转化函数
- 内存操作函数
- memcpy函数
- memcpy函数模拟实现
- memmove函数
- memmove函数模拟实现
- memset函数
- memcmp函数
铁汁们,今天给大家分享一篇字符函数和字符串函数全面知识总结,来吧,开造⛳️
前期背景
在C语言具有许多数据类型,但不具有字符串类型,字符串通常放在字符指针或字符数组中,因为在C语言中对字符和字符串的处理很频繁,从而延伸出许许多多有关字符串的函数。
求字符串长度函数
strlen函数
功能:求字符串中元素的个数,不包括末尾的 ’ \0 '。 头文件为#include<string.h>。
字符串是以 ’ \0 '作为结束标志,strlen函数返回的是在字符串中 ’ \0 '前面的总元素个数(不包括 ’ \0 ') 。
注意:
1.strlen函数的返回值为 size_t ,无符号整形(unsigned int)。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcdef"; //元素个数为6个
const char* str2 = "bbb"; //元素个数为3个
if (strlen(str2) - strlen(str1) > 0) //两个无符号进行运算,则结果也为无符号
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
图解:
2.参数指向的字符串必须要以 ‘\0’ 为结束标志,否则,strlen会越界一直找’ \0 ',直到遇到 ’ \0 ’ 停止,结果为随机值。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
char arr1[8] = "abcde";
char arr2[5] = {'a','b','c','d','e'};
printf("%d\n",strlen(arr1));
printf("%d\n",strlen(arr2));
return 0;
}
strlen函数三种模拟实现
1.计数器
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int my_strlen(const char* arr) //计数器模拟实现strlen
{
assert(arr); //断言,检查指针的有效性,防止对空指针进行解引用、加减整数操作
int count = 0;
while (*arr)
{
count++;
arr++;
}
return count;
}
int main()
{
char arr[10] = "hello bit";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
2.指针-指针(计算的是两指针之间的元素个数)
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int my_strlen(const char* arr) //指针-指针模拟实现strlen
{
assert(arr); //断言,检查指针的有效性,防止对空指针进行解引用、加减整数操作
char* start = arr;
char* end = arr;
while (*end)
end++;
return end - start;
}
int main()
{
char arr[10] = "hello bit";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
3.递归
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int my_strlen(const char* arr) //递归模拟实现strlen
{
assert(arr); //断言,检查指针的有效性,防止对空指针进行解引用、加减整数操作
if (*arr != '\0')
return my_strlen(arr + 1) + 1;
else
return 0;
}
int main()
{
char arr[10] = "hello bit";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
长度不受限制的字符串函数
strcpy函数
功能:将源头字符串的内容拷贝到目标字符串,包括末尾的 ‘\0’(相当于从前往后进行覆盖)。 头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
char dest[20] = "abcdefg";
char source[6] = "hello";
strcpy(dest, source + 2); //可以从源字符串的任意位置处开始拷贝
printf("%s", dest);
return 0;
}
=注意:
1.会将源头字符串的 '\0’拷贝进去。2.目标空间要足够大,足以存放源头字符串。
3.目标空间必须是可变的(不能被const修饰)。
4.源头字符串必须以 ‘\0’ 为结束标志(因为strcpy拷贝完\0’后就立即停止拷贝)。
strcpy函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* source)
{
assert(dest&&source); //断言,检查指针的有效性,防止对空指针进行解引用、加减整数操作
char* ret = dest;
while (*dest++ = *source++)
{
;
}
return ret;
}
int main()
{
char dest[20] = "abcdefg";
char source[6] = "hello";
char* ret = my_strcpy(dest, source);
printf("%s", ret);
return 0;
}
strcpy在内存中的拷贝过程:
strcat函数
功能:将源头字符串的内容追加到目标字符串的后面(从目标串的’\0’位置开始向后追加)。 头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
char dest[30] = "heihei";
char source[] = " hello";
strcat(dest, source);
printf("%s", dest);
return 0;
}
注意:
1.目标字符串必须可修改。
2.源字符串必须含有 '\0’为结束标志。
3.目标空间要足够大,足以容纳源头字符串的内容。
问题:字符串是否可以自己给自己追加?
答:字符串不可以自己给自己追加,因为源头字符串的’\0’会被覆盖,造成源头字符串会一直向后死循环追加。strcat遇到源头字符串的 ‘\0’就停止追加,并且源头字符串末尾的’\0’也会被追加过去。
strcat函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
char* my_strcat(char* dest,const char* source)
{
assert(dest&&source); //断言,检查指针的有效性,防止对空指针进行解引用、加减整数操作
char* ret = dest;
while (*dest) //找目标字符串中的'\0'
{
dest++;
}
while (*dest++ = *source++)
{
;
}
return ret;
}
int main()
{
char dest[30] = "abcde";
char source[] = "hello bit";
char* ret = my_strcat(dest, source);
printf("%s", ret);
return 0;
}
strcmp函数
功能:用来比较两字符串的大小,两字符串不能直接用==进行比较。 头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
char s1[] = "abcdefg";
char s2[] = "abcde";
int ret=strcmp(s1, s2);
printf("%d\n", ret);
return 0;
}
问题:strcmp如何判断两字符串大小?
答:两个字符串从前向后依次比较两个字符,判断是否相等,相等,则继续比较下一个字符,直到有一个串遇到’\0’,并比较完’\0’,就停止。若不相等,比较两字符的ASCII值,ASCII值大的串,字符串大。《—等价于—》ASCII值的比较
strcmp返回值三种情况:
a.字符串1(前面的参数)大于 字符串2(后面的参数),则返回大于0的数。
b.字符串1(前面的参数)小于 字符串2(后面的参数),则返回小于0的数。
c.字符串1(前面的参数)等于 字符串2(后面的参数),则返回等于0的数。
strcmp函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int my_strcmp(const char* s1, const char* s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1 - *s2;
}
int main()
{
char s1[] = "abcde";
char s2[] = "abcdef";
int ret = my_strcmp(s1, s2);
printf("%d", ret);
return 0;
}
长度受限制的字符串函数
strncpy函数
功能: 头文件为#include<string.h>。
情况1:源字符串的长度>num,将源字符串的前num个字符拷贝到目标字符串中;
情况2:源字符串的长度<num将源字符串全部拷贝到目标字符串中,包括源字符串末尾的’\0’,源字符串拷贝完后,在目标字符串的后面一直追加 ‘\0’,直到num停止。
情况1代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
int num = 5; //源字符串的长度>num
char dest[30] = "heheabcd";
char source[] = "lala hello";
strncpy(dest, source, num);
printf("%s", dest);
return 0;
}
情况2代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
int num = 20; //源字符串的长度<num
char dest[30] = "heheabcdefghi";
char source[] = "lala hello";
strncpy(dest, source, num);
printf("%s", dest);
return 0;
}
内存图:
strncpy函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
char* my_strncpy(char* dest,const char* source, int num)
{
assert(dest && source);
char* ret = dest;
while (num--)
{
if (*source != '\0') //情况1,源字符串的长度>num
*dest++ = *source++;
else //情况2,源字符串的长度<num,源字符串拷贝完后,在目标字符串后一直追加'\0'
*dest++ = '\0';
}
return ret;
}
int main()
{
int num = 5;
char dest[30] = "heheabcdefghi";
char source[] = "lala hello";
char* ret = my_strncpy(dest, source, num);
printf("%s\n", dest);
return 0;
}
strncat函数
功能: 头文件为#include<string.h>。
情况1:源字符串的长度>num,将源字符串的前num个字符追加到目标字符串后面,最后在目标字符串后面自动补上 ‘\0’;
情况2:源字符串的长度<num将源字符串全部追加到目标字符串后面,包括源字符串末尾的’\0’,则就停止追加。
情况1代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
int num = 5; //情况1,源字符串的长度>num
char dest[30] = "abcde\0lalalalalala";
char source[] = "hello world";
strncat(dest, source, num);
printf("%s\n", dest);
return 0;
}
情况2代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
int num = 20; //情况2,源字符串的长度<num
char dest[30] = "abcde\0lalalalalalalalalalala";
char source[] = "hello world";
strncat(dest, source, num);
printf("%s\n", dest);
return 0;
}
strncat函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* source,int num)
{
assert(dest && source);
char* ret = dest;
while (*dest)
dest++;
while (num--)
{
*dest = *source;
if (*source == '\0') //清款2
break;
dest++;
source++;
}
if (*source != '\0') //情况1
*dest = '\0';
return ret;
}
int main()
{
int num =5;
char dest[30] = "abcde\0lalalalalalalalalalala";
char source[] = "hello world";
char* ret=my_strncat(dest, source, num);
printf("%s\n", ret);
return 0;
}
strncmp函数
功能:比较前num个字符的大小, 头文件为#include<string.h>。
比较到在num范围内有一个串结束、两串中有一个字符不相等,前num个字符全部比完,就停止比较了。
返回值三种情况:同strcmp函数返回值情况相同。
strncmp函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int my_strncmp(const char* s1,const char* s2, int num)
{
assert(s1&&s2);
while (num--)
{
while (*s1 == *s2)
{
if(*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1-*s2;
}
}
int main()
{
int num = 3;
char arr1[] = "abcdef";
char arr2[] = "abed";
int len = my_strncmp(arr1, arr2,num);
printf("%d\n", len);
return 0;
}
字符串查找
strstr函数
功能:在string指向的字符串中查找strCharSet指向的字符串是否出现,出现,则返回strCharSet指向的字符串在tring指向的字符串第一次出现的起始位置,若未出现,则返回NULL。 头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
char arr[] = "abcdefbcdef";
char src[] = "bcdef";
char* ret=strstr(arr, src);
printf("%s\n", ret);
return 0;
}
strstr函数模拟实现
版本1:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
char* my_strstr(char* dest, char* src)
{
assert(dest && src);
char* cur = dest;
char* s1;
char* s2;
while (*cur)
{
s1 = cur;
s2 = src;
while (*s2&&*s1)
{
if (*s1 != *s2)
break;
s1++;
s2++;
}
if (*s2 == '\0')
return cur;
cur++;
}
}
int main()
{
char arr[] = "abbbcdefbcdef";
char src[] = "bcdef";
char* ret=my_strstr(arr, src);
printf("%s\n", ret);
return 0;
}
版本2:枚举法(效率更高)
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
char* my_strstr(char* dest, char* src)
{
assert(dest && src);
char* ret = dest;
int len1 = strlen(dest);
int len2 = strlen(src);
int i=0;
for ( ; i + len2 <= len1; i++)
{
int j = 0;
for (; j < len2; j++)
{
if (dest[i+j] != src[j])
break;
}
if (j == len2)
return (ret + i);
}
return NULL;
}
int main()
{
char arr[] = "abbbcdefbcdef";
char src[] = "bcdef";
char* ret=my_strstr(arr, src);
printf("%s\n", ret);
return 0;
}
strtok函数
功能:用于处理带标记的字符串。头文件为#include<string.h>。
注意:
1.参数2strDelimit指向的字符串定义了用作分隔符的集合,注意参数2必须是字符串,而’\0‘代表的是字符串结束符,通常用于标记字符串的末尾,并不作为分隔符。
2.strToken指向的字符串,它包含了0个或者多个strDelimit指向字符串的分隔符标记。
3.strtok函数找到strDelimit指向字符串中的下一个分隔符的标记,并会自动保存这个标记符在字符串的位置,返回一个指向这个标记的指针。
4.当第一个参数不为NULL,找到第一个分割符标记,该函数内部会自动保存这个标记符在字符串的位置,进行第二次使用strtok函数时,函数的第一个参数为NULL,该函数会从上次strtok函数保存的上个分割标记符后一个位置处查询下一个分割标记符并保存下一个分割标记符所在字符串中的位置,直到不存在分隔符停止查找。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
char arr1[] = "naifu,naiyou.fsq#zzx";
const char* arr2= ",.#";
for (char* ret = strtok(arr1, arr2); ret != NULL; ret=strtok(NULL, arr2))
{
printf("%s\n", ret);
}return 0;
}
错误信息报告
strerror函数
功能:返回错误码所对应的错误信息。头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h> //strerror库函数的头文件
#include<errno.h> //必须包含的头文件
int main()
{
FILE* p = fopen("lala.txt", "r");
if (p == NULL)
{
printf("%s\n", strerror(errno)); //errno存放错误码
return 1;
}
fclose(p);
p = NULL;
return 0;
}
strerror函数可以用于确定库函数在执行时发生的错误的原因。当库函数遇到错误时,它会设置一个全局变量errno,C语言会将错误码存放在errno这个全局变量中,strerror函数使用errno确定特定错误码对应的错误消息。
perror函数
功能:返回错误码所对应的错误信息。头文件为#include<stdio.h>。
与strerror作用效果相同,只是perror会输出string指向的字符串内容在加个冒号(:),在输出错误码所对应的错误信息,在文件操作中,确定错误信息是什么,常用perror函数。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
FILE* p = fopen("lala.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
fclose(p);
p = NULL;
return 0;
字符分类函数
头文件为#include<ctype.h>。
大小写转化函数
头文件为#include<ctype.h>
内存操作函数
memcpy函数
功能:从src位置向后拷贝count个字节的内容到dest指向的字符串中(遇到\0不会停下来)。若拷贝的字节数大于源头字符串的总字节数,超过的字节数拷贝的内容为随机值。 头文件为#include<string.h>。
注意:memecpy适用于不重叠数据或者无关联的数据拷贝。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
int count = 4;
int arr[10] = { 1,2,3,4,5,6 };
int src[6] = { 7,8,9,10,11,12 };
memcpy(arr, src, sizeof(arr[0])*count);
for (int i = 0; i <10; i++)
printf("%d ", arr[i]);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<string.h>
int main()
{
int count = 3;
int arr[10] = { 1,2,3,4,5,6 };
int src[3] = { 7,8,9};
memcpy(arr, src, sizeof(arr[0])*count);
for (int i = 0; i <10; i++)
printf("%d ", arr[i]);
return 0;
}
memcpy函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
void* my_memcpy(void* dest, void* src, int count) //适用于任意类型
{
assert(dest && src);
while (count--)
{
*(char*)dest = *(char*)src; //对空指针进行解引用操作,具有临时性,有效时长仅在其所在的语句中
(char*)dest = (char*)dest + 1; //(char*)dest++,因优先级:++>(),dest(NULL)先与++结合,不能对空指针进行加减、解引用操作
(char*)src = (char*)src + 1;
}
return ;
}
int main()
{
int count = 4;
int arr[10] = { 1,2,3,4,5,6,7,8 };
int src[6] = { 9,10,11,12 };
int len = sizeof(arr) / sizeof(arr[0]);
my_memcpy(arr, src, sizeof(arr[0] )* count);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
问题:memcpy可以拷贝重叠数据吗?
答:memcpy不可以拷贝重叠数据,会造成数据被覆盖。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
#include<string.h>
void* my_memcpy(void* dest, void* src, int count) //适用于任意类型
{
assert(dest && src);
while (count--)
{
*(char*)dest = *(char*)src; //对空指针进行解引用操作,具有临时性,有效时长仅在其所在的语句中
(char*)dest = (char*)dest + 1; //(char*)dest++,因优先级:++>(),dest(NULL)先与++结合,不能对空指针进行加减、解引用操作
(char*)src = (char*)src + 1;
}
return ;
}
int main()
{
int count = 5;
int arr[10] = { 1,2,3,4,5,6,7,8 };
int src[6] = { 9,10,11,12 };
int len = sizeof(arr) / sizeof(arr[0]);
my_memcpy(arr+2, arr, sizeof(arr[0] )* count); //memcpy拷贝重叠数据
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
memmove函数
功能:从src位置向后拷贝count个字节的内容到dest指向的字符串中(遇到\0不会停下来)。若拷贝的字节数大于源头字符串的总字节数,超过的字节数拷贝的内容为随机值。 头文件为#include<string.h>。
注意:memmove既适用于重叠数据或者有关联的数据拷贝,又适用于不重叠数据或者无关联的数据拷贝。
重叠数据或者有关联的数据拷贝:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int main()
{
int count = 5;
int arr[10] = { 1,2,3,4,5,6,7,8 };
int len = sizeof(arr) / sizeof(arr[0]);
memmove(arr+2, arr, sizeof(arr[0] )* count);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
不重叠数据或者无关联的数据拷贝:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
int main()
{
int count = 5;
int arr[10] = { 1,2,3,4,5,6,7,8 };
int src[5] = { 9,10,11,12,13 };
int len = sizeof(arr) / sizeof(arr[0]);
memmove(arr,src, sizeof(arr[0] )* count);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
memmove函数模拟实现
memmove模拟实现图:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, int count)
{
void* ret = dest;
if (dest < src) //从前往后拷贝
{
while (count--)
{
*((char*)dest) = *((char*)src);
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else //从后往前拷贝
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
int main()
{
int count = 5;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr+2, arr, sizeof(int) * count); //重叠数据
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
问题:memmove与memcp有何区别?
答:memmove既可以处理重叠数据,又可处理不重叠数据(100%)。 memcpy被规定来只能用来处理不重叠或者无关联的数据(60%),但在有些编译器上,memcpy也可用来处理重叠数据,所以一般处理重叠数据的拷贝选择memmove,处理不重叠的数据选择memcpy。
memset函数
功能:将dest指向的内存空间的前count字节的内容全部设置为c。 头文件为#include<string.h>。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int count = 5;
memset(arr, 0, sizeof(int) * count);
for (int i = 0; i < 10; i++)
printf("%d ", arr[i]);
return 0;
}
应用场景:常适用于初始化一块连续的内存空间,eg:静态版通讯录实现。
memcmp函数
功能:将dest指向的内存空间前count字节的内容与src指向的内存空间前count字节的内容相比较,比较到在count字节范围内有一个空间结束、两空间中有一个字节不相等,前num个字节全部比完,就停止比较了,返回值与strncmp相同。
铁铁们,字符函数和字符串函数知识总结就到此结束啦,若博主有不好的地方,请指正,欢迎铁铁们留言,请动动你们的手给作者点个👍鼓励吧,你们的鼓励就是我的动力✨