在C语言中,有一种特殊的数据类型,即字符串类型。C 并没有专门定义一个字符串类型,这对我们使用字符串造成了一定的麻烦。但是,C标准库<string.h> 中定义了各种字符串函数,这对于我们来说是一件值得庆幸的事情。
本片着重讲解一些常用的字符串函数,以及它们的实现方法思路,并尝试自己独自模拟实现它们,以便于对字符串操作有更深的理解。
字符串简介
C语言中的字符串类型是以字符数组的形式表示的,即用一组字符数组来表示一个字符串,例如:
cpp
char str[10] = "hello"; // 定义一个长度为10的字符数组,初始化为"hello"
其中,char
表示字符类型,str
表示字符数组的名称,10
表示字符数组的长度,"hello"
表示初始值。
C语言中的字符串还可以使用字符串指针来表示,例如:
cpp
char *str = "hello"; // 定义一个指向字符数组的指针,指向"hello"
其中,char *
表示字符指针类型,str
表示指针变量的名称,"hello"
表示字符串常量,也就是一段字符数组的初始值。注意,使用字符串指针表示字符串时,需要保证指针指向的字符串常量是合法的,并且不能修改它的值,否则会发生未定义的行为。
头文件<string .h>
<string.h>是C语言标准库中的一个头文件,提供了一些字符串处理相关的函数和宏。
统一说明:
统一说明:
养成良好的代码习惯:
1.在实现字符串操作的时候,如果不希望字符串被改变,在函数形参前加上const,提高代码的健壮性。
2.使用指针前判断将要解引用的指针是否是空指针,assert进行断言。
++对于每一个函数使用的注意事项放在每个函数模拟实现的末尾!++
strlen
作用:
返回字符串中 '\0' 之前的所有字符数。
函数原型:
函数参数:
str是存放要操作的字符串的地址的指针
返回值类型:
size_t(表示无符号整型)的理解:由实际意义,返回的字符数不会是负数;并且看到 size_t的size,就可以知道此类型是表示大小,尺寸的类型。
模拟实现:
法一:计数变量法:
cpp
#include<stdio.h>
int my_strlen(char*p)
{
int c = 0;
while(*p)
{
c++;
p++;
}
return c;
}
int main()
{
char arr[] = "ahufkh";
printf("%d",my_strlen(arr));
return 0;
}
法二:
指针相减法:
cpp
#include<stdio.h>
int my_strlen(char* p)
{
char* start = p;
while(*p)
{
p++;
}
return p - start;
}
int main()
{
char arr[] = "ahufkh";
printf("%d",my_strlen(arr));
return 0;
}
对size_t的补充:
易错点:
对于无符号整数,运算结果小于0,由于不存在符号位,所以结果会被当做很大的整数。
e.g.1
cpp#typedef unsigned int uint int main() { uint a = 3; uint b = 6; uint c = a - b;//此时c是很大的整数 }
结果为负值,c被当作很大的整数。
strcpy
作用:
将源字符串拷贝到目的地字符串。
函数原型:
函数参数:
1.char* dest即目的地字符串,const char* sou 即源字符串。
返回值类型:
返回拷贝后目的地字符串的地址。
模拟实现:
cpp
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest,const char* sou)
{
char* s = dest;
assert(dest && sou);
while(*dest++ = *sou++)
{
;
}
return s;
}
int main()
{
char arr1[] = "abcdefghijk";
char arr2[] = "iii";
char* s = my_strcpy(arr1,arr2);
printf("%s",s);
return 0;
}
(记得const与assert断言)
注意:
1.sou字符串必须有 '\0' 作为结尾。
2.dest字符串必须足够大,防止越界;并且可修改,不是常量字符串。
strcat
作用:
将源字符串拷贝到目的地字符串中,并且源字符串的的一个字符覆盖掉目的地字符串中的 '\0' 。
函数原型:
函数参数:
1.const 修饰的源字符串和目标字符串。
返回值类型:
copy后的目标字符串的地址。
模拟实现:
cpp
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest,const char* sou)
{
char* start = dest;
assert(dest && sou);
while(*dest)
{
dest++;
}
while(*dest++ = *sou++)
{
;
}
return start;
}
int main()
{
char arr[50] = "abc";
char arr1[] = "defg";
char* p = my_strcat(arr,arr1);
printf("%s",p);
return 0;
}
注意:
1.cou与dest字符串必有 '\0' 作为结束标志。
2.目标字符串必须足够大。
3.strcat的两个函数参数不能相同。(一个字符串对自己追加,那么一开始,自己的结束表示就被覆盖了,这样将造成死循环。)
strcmp
作用:
比较两个字符串的大小;
函数原型:
函数参数:
两个const 修饰的字符串
返回值类型:
整型数值------
if第一个>第二个,返回值>0;
if第一个=第二个,返回值=0;
if第一个<第二个,返回值<0。
模拟实现:
cpp
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1,const char* str2)
{
while(*str1 == *str2)
{
if(*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return str1 -str2;
}
int main()
{
char arr1[] = "abcde";
char arr2[] = "abb";
int ret = my_strcmp(arr1,arr2);
if(ret > 0)
{
printf("arr1 > arr2");
}
else if(ret == 0)
{
printf("arr1 == arr2");
}
else
{
printf("arr1 < arr2");
}
return 0;
}
strstr
作用:
返回str2在str1中第一次出现的位置;若找不到,则返回NULL;字符串的比较匹配不包含 '\0' ,但是以 ' \0 ' 为结束标志。
函数原型:
函数参数:
查找的样本字符串和被查找的目标字符串。
返回值类型:
str1中str2第一次出现的位置。
模拟实现:
cpp
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* p1,const char* p2)
{
assert(p1 && p2);
const char* cur = p1;
const char* s1 = NULL;
const char* s2 = NULL;
while(*cur)
{
s1 = cur;
s2 = p2;
while(*s1 == *s2 && s1 && s2)
{
s1++;
s2++;
}
if(*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
return NULL;
}
int main()
{
char arr1[] = "abbbbbcdefg";
char arr2[] = "bbc";
char* p = my_strstr(arr1,arr2);
printf("%s",p);
return 0;
}
思路:
对于停下来的情况,有:
1.s1找到'\0'的同时s2也找到'\0',则找到;若s2没有到'\0',则没有找到;
2.s2到'\0',找到。
3.*s1 != *s2
于是,对s1中的每一个位置向后匹配s2的字符,如果有一个匹配失败,则从s1的下一个位置开始匹配。
完~
未经作者同意禁止转载