目录
输入字符串的相关函数:
scanf、gets、fgets
(一)strlen
1、strlen 相关细节
计算一个字符串的长度(‘\0’之前的长度)
例如字符串“abcd”,它的长度为4。实际上,"abcd"是“abcd\0”,在 \0 之前有4个字符,所以该字符串的长度是4.
还要注意的是,strlen的返回值是size_t类型的,即unsigned int 类型的,这也就代表用strlen函数求出的长度结果一定是一个整数。
易错点:
#include <stdio.h>
#include <string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
可能会有人认为代码运行的结果是<=, 但实际上运行结果是>
导致这种现象的原因就是strlen 的返回值,strlen("abc")的运算结果是3,strlen("abcdef")的运算结果是6,但是3和6的数据类型是unsigned int 类型的,所以两者运算的结果也是unsigned int 类型的,即运算结果恒大于0.
2、 strlen 模拟实现
计数器的方法:
#include <stdio.h>
int Strlen(char* s)
{
int count = 0;
while (*s != '\0')
{
count++;
s++;
}
return count;
}
int main()
{
char arr[] = "ABCDEF";
int ret = Strlen(arr);
printf("%d", ret);
return 0;
}
递归的方法
#include <stdio.h>
int Strlen(char* s)
{
if (*s == '\0')
return 0;
else
return 1+Strlen(s+1);
}
int main()
{
char arr[] = "ABCDEF";
int ret = Strlen(arr);
printf("%d", ret);
return 0;
}
指针向减的运算:
#include <stdio.h>
int Strlen(char* s)
{
char* start = s;
char* end = s;
while (*end != '\0')
end++;
return end - start;
}
int main()
{
char arr[] = "ABCDEF";
int ret = Strlen(arr);
printf("%d", ret);
return 0;
}
易错写法:
++的运算优先级高于解引用,所以先会进行++操作在进行解引用,但又因为++为后置++,所以解引用操作结束后才会把这个1加上,此时end指向的是\0后面的空间。
while (*end++ != '\0');
当我们换成这种写法,就正确了
while (*++end != '\0');
(二)strcpy
1、strcpy 相关细节
strcpy 会把源字符串拷贝到目标数组中,拷贝过程以\0 作为结束标志,拷贝的内容包括源字符串中的\0;这里会存在两种情况:
1)如果一个字符串中有一个\0 ,\0后面还有内容,比如“Hello\0World!”,strcpy 函数只能够将Hello\0拷贝到目标数组中,World 不会被拷贝过去。
2)如果source中没有\0, 使用strcpy会拷贝失败。
这里需要注意的是,不能够写成以下两种错误写法:
1)
char name[20] = { 0 };
name = "zhangsan";
name是数组名,也就是一个地址,而地址是一个常量是不能被修改的,所以不能进行赋值操作。
2)
char name[3] = "li";
char* p = "zhangsan";
strcpy(p, name);
这是因为p指向的是一个常量字符串,常量字符串是不能被修改的。
2、strcpy 模拟实现
char* my_strcpy(char* destination, const char* source)
{
assert(destination);
assert(source);
char* pmove = destination;
while (*pmove++ = *source++);
return destination;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
3.1 strncpy 相关细节
size_t num
Maximum number of characters to be copied from source.
size_t is an unsigned integral type.
3.2 strncpy 实例
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
strncpy(arr2, arr1, sizeof(char)*strlen(arr1)-1);
printf("%s\n", arr2);
return 0;
}
(三)strcat
1、strcat 相关细节(与strcpy相似)
1. 源字符串必须以 '\0' 结束。
2. 目标空间必须有足够的大,能容纳下源字符串的内容。
3. 目标空间必须可修改。
2、strcat 模拟实现
第一步:先找到destination 的字符串末尾;
第二步:将source拷贝到destination的末尾。
char* my_strcat(char* destination, const char* source)
{
assert(destination && source);
char* pmove = destination;
while (*pmove != '\0')
pmove++;
while (*pmove++ = *source++);
return destination;
}
int main()
{
char arr1[20] = "Hello ";
char arr2[] = "World!";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
这里还需要注意一个点:当给一个字符串追加自己时,会出现一下问题:
因为自己追加自己时,会把原字符串中的\0覆盖掉,导致会一直进行拷贝,直到空间用完然后越栈。
如果想要自己给自己追加时,可以使用 strncat,该函数不会出现上述现象。
3.1 strncat 相关细节
相较于 strcat 函数,strncat 函数多了一个要拷贝数据的大小的参数(单位是字节)
size_t num
Maximum number of characters to be appended
size_t is an unsigned integral type
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "Hello ";
strncat(arr1, arr1, strlen(arr1)*sizeof(char));
printf("%s\n", arr1);
return 0;
}
3.2 strncat 模拟实现
其实能不能实现自我追加,完全看编译器是如何实现 strcat 和 strncat 函数的,这里只需要分两种情况分别实现就可以了:
① 当 strncat 的两个字符串参数不为同一个字符串时,实现方法就如同上述 strcat 的模拟实现一样;
② 当 strncat 的两个字符串参数为同一个字符串时, 只需要创建临时缓冲区保存一下原字符串,再用临时缓冲区的内容追加到原字符串中即可(这样就不会出现追加覆盖原先内容的情况了)。
具体代码如下:
char* my_strncat(char* destination, const char* source, size_t num)
{
assert(destination && source);
if (destination == source)
{
char* pmove = destination;
char a[30] = { 0 };
char* temp = a;
strcpy(temp, destination);
while (*pmove != '\0')
pmove++;
while (*temp != '\0' && num > 0)
{
*pmove = *temp;
pmove++;
temp++;
num--;
}
*pmove = '\0';
}
else
{
char* pmove = destination;
// 将指针移动到目标字符串的末尾
while (*pmove != '\0')
pmove++;
// 将源字符串的最多n个字符拼接到目标字符串的末尾
while (*source != '\0' && num > 0)
{
*pmove = *source;
pmove++;
source++;
num--;
}
*pmove = '\0';
}
return destination;
}
(四)strcmp
1. strcmp 的相关细节
strcmp 函数是C语言标准库中的一个字符串比较函数,用于比较两个字符串的大小关系。它的函数原型如下:
int strcmp(const char* str1, const char* str2);
strcmp 函数接收两个参数,即要比较的两个字符串str1
和str2
,并返回一个整数值,表示两个字符串的大小关系。返回值的含义如下:
- 如果
str1
和str2
相等,返回值为0。- 如果
str1
大于str2
,返回值大于0。- 如果
str1
小于str2
,返回值小于0。
strcmp 函数的比较规则如下:
- 首先,从两个字符串的首字符开始逐个比较,直到遇到不相等的字符或者达到字符串的结束符(
\0
)。- 如果遇到不相等的字符,比较它们的ASCII码值,返回差值(
str1[i] - str2[i]
)(不过在VS中,大于0是返回 1 ,小于0是返回 -1 )。- 如果两个字符串的长度不同,且较短的字符串在相同位置上的字符都与较长字符串相等,则较长字符串更大。
2. strcmp 模拟实现
根据 strcmp 函数的比较规则,可以一个一个字符遍历比较两者的ASCII码值,直到遇到不相等的字符或者达到字符串的结束符(\0
)即可。
具体代码如下:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
char* pmove1 = str1;
char* pmove2 = str2;
while (*pmove1 != '\0' && *pmove2 != '\0')
{
int count = *pmove1 - *pmove2;
if (count > 0)
return 1;
else if(count < 0)
return -1;
else
{
pmove1++;
pmove2++;
}
}
if (*pmove1 != '\0')
return 1;
else if (*pmove2 != '\0')
return -1;
else
return 0;
}
3. strncmp 的相关细节
还有 strncmp 函数,有了以上基础,模拟实现这个函数也是很容易的,这里我就不实现了。
(五)strstr 函数
1. strstr 的相关细节
strstr 函数是C语言标准库中的一个字符串查找函数,用于在一个字符串中查找另一个字符串的出现位置。它的函数原型如下:
char* strstr(const char* haystack, const char* needle);
strstr
函数接收两个参数,即待搜索的字符串haystack
和要查找的目标字符串needle
,并返回一个指向第一次出现目标字符串的位置的指针。如果目标字符串不存在于待搜索的字符串中,则返回NULL
。
strstr 函数的工作原理如下:
- 首先,从待搜索的字符串
haystack
的第一个字符开始,逐个比较字符。- 如果当前字符与目标字符串
needle
的第一个字符相等,则继续比较后续字符,直到找到完全匹配的子字符串或者遇到不匹配的字符。- 如果找到了完全匹配的子字符串,返回指向该子字符串的指针。
- 如果遍历完待搜索的字符串
haystack
仍未找到匹配的子字符串,则返回NULL
。
2. strstr 模拟实现
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
int len = (int)strlen(str2);
while (*str1 != '\0')
{
int count = 0;
int i = 0;
for (i = 0; i < len; i++)
{
if (*(str1 + i) == *(str2 + i))
count++;
else
break;
}
if (count == len)
return str1;
else
str1++;
}
return NULL;
}
(六)strtok
1. strtok 的相关细节
strtok 函数是C语言标准库中的一个字符串分割函数,用于将一个字符串按照指定的分隔符进行分割。它的函数原型如下:
char* strtok(char* str, const char* delimiters);
strtok 函数接收两个参数,即待分割的字符串str
和分隔符字符串delimiters
。在第一次调用时,需要将待分割的字符串作为参数传入;之后的调用中,将第一个参数设为NULL
即可。
strtok
函数的工作原理如下:
- 在第一次调用时,
strtok
函数会在待分割的字符串str
中查找第一个不属于分隔符的字符,并将其作为分割后的第一个子字符串的起始位置。- 之后,
strtok
函数会继续查找下一个属于分隔符的字符,并将其替换为字符串结束符\0
,以分割当前的子字符串。- 在后续的调用中,
strtok
函数会继续从上一次分割的位置开始,查找下一个不属于分隔符的字符,重复上述步骤,直到分割完所有的子字符串。- 如果无法找到下一个不属于分隔符的字符,则返回
NULL
,表示分割完成。
由函数的工作原理可以知道,分割一个字符串,第一次调用参数是带分割的字符串和分隔符字符串;第二次调用参数是NULL和分隔符字符串。所以可以利用for循环来调用函数实现任何长度的待分割的字符串进行分割:
#include <stdio.h>
#include <string.h>
int main()
{
char pa[20] = "I am a student!";
const char* pb = " ";
char temp[20] = { 0 };
char* str = NULL;
strcpy(temp, pa);
for (str = strtok(temp, pb); str != NULL;str = strtok(NULL, pb))
{
printf("%s\n", str);
}
return 0;
}
2. strtok 模拟实现
char* my_strtok(char* str, const char* delimiters)
{
static char* token = NULL;
char* start = NULL;
if (str != NULL) // 第一次调用strtok
{
token = str;
}
// 跳过开头是分隔符的字符
while (*token != '\0' && strchr(delimiters, *token) != NULL)
{
token++;
}
// 如果token已经达到字符串末尾,直接返回NULL
if (*token == '\0')
return NULL;
// 记录分隔符前字符串的起始位置
start = token;
// 找到下一个分隔符的位置并将分隔符替换为\0
token = strpbrk(start, delimiters);
if (token != NULL)
{
*token = '\0';
token++;
}
return start; // 返回分隔符前字符串的起始位置
}
在实现这个函数时,我用到了一下两个字符串函数:
3.1 strchr 的相关细节
strchr 函数是C语言标准库中的一个字符串查找函数,用于在一个字符串中查找指定字符的第一个出现位置。它的函数原型如下:
char* strchr(const char* str, int character);
strchr 函数接收两个参数,即待查找的字符串str
和要查找的字符character
。它会在str
字符串中查找第一个等于character
的字符,并返回该字符在str
中的地址(指针)。
strchr 函数的工作原理如下:
strchr
函数从str
字符串的开头开始逐个字符地比较,直到找到第一个等于character
的字符或者到达字符串结尾(即遇到空字符\0
)。- 如果找到了等于
character
的字符,则返回该字符在str
中的地址(指针)。- 如果未找到等于
character
的字符,则返回NULL
。
3.2 strpbrk 的相关细节
strpbrk 函数是C语言标准库中的一个字符串查找函数,用于在一个字符串中查找包含指定字符集合中任意字符的第一个出现位置。它的函数原型如下:
char* strpbrk(const char* str, const char* charset);
strpbrk 函数接收两个参数,即待查找的字符串str
和包含待查找字符集合的字符串charset
。它会在str
字符串中查找第一个包含charset
中任意字符的字符,并返回该字符在str
中的地址(指针)。
strpbrk 函数的工作原理如下:
strpbrk
函数从str
字符串的开头开始逐个字符地比较,直到找到第一个包含在charset
中的字符或者到达字符串结尾(即遇到空字符\0
)。- 如果找到了包含在
charset
中的字符,则返回该字符在str
中的地址(指针)。- 如果未找到包含在
charset
中的字符,则返回NULL
。
以上两个函数我就不一一实现了,读者可以自己思考一下。
好了,以上就是我所要分享关于C语言字符串相关函数及其模拟实现的所有内容,我所实现的字符串函数,效率不是很高(基本上都是遍历实现的,大佬勿喷),比如 strstr 函数,如果使用KMP算法效率会大大提高。
如果你觉得这篇博客对你有帮助的话 ,希望你能够给我点个赞,鼓励一下我。感谢感谢……