本文主要介绍处理字符和字符串的库函数的功能及模拟实现。
1.对库函数按照功能可分为以下几类:
(1)求字符串长度:strlen;
(2)长度不受限制的字符串函数:strcpy、strcat、strcmp;
(3)长度受限制的字符串函数:strncpy、strncat、strncmp;
(4)字符串查找:strstr、strtok;
(5)字符操作:iscntrl、isspace、isdigit、isxdigit、islower、isupper、isalpha、isalnum、ispunct、isgraph、isprint;
(6)内存操作函数:memcpy、memmove、memset、memcmp;
2.功能介绍及模拟实现
C语言中对字符和字符串的处理比较麻烦的,而且C语言没有字符串类型,所以字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数。
(1)strlen函数介绍及模拟实现
介绍:用来计算字符串的长度,字符串已经'\0' 作为结束标志,strlen函数返回的是在字符串中'\0' 前面出现的字符个数(不包含'\0' )。参数指向的字符串必须要以'\0' 结束。返回值为size_t,是无符号的。
size_t strlen ( const char * str );
模拟实现:
方法一:计数器方式
int my_strlen(const char* str)
{
char* s = (char*)str;
int i = 0;
while (*s)
{
s++;
i++;
}
return i;
}
方法二:不创建临时变量计数器
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
方法三:指针的方式
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
(2)strcpy函数介绍及模拟实现
介绍:strcpy可以将以'\0'结束的字符串复制到另一个地址中,返回值的类型为char*,源字符串必须以'\0' 结束,会将源字符串中的'\0' 拷贝到目标空间,目标空间必须足够大,以确保能存放源字符串,目标空间必须可变。
char* strcpy(char * destination, const char * source );
模拟实现:需要注意参数顺序,函数的功能,停止条件,通过assert断言保证地址有效性,const修饰源地址指针。
char* my_strcpy(const char * s1,const char* s2)
{
assert(s1 != NULL);
assert(s2 != NULL);
char* p1 = (char*)s1;
char* p2 = (char*)s2;
while (*p1 = *p2)
{
p1++;
p2++;
}
return *s1;
}
(3)strcat函数介绍及模拟实现
介绍:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”),源字符串必须以'\0' 结束,目标空间必须有足够的大,能容纳下源字符串的内容,目标空间必须可修改。
char * strcat ( char * destination, const char * source );
模拟实现:
char* my_strcat(char * dest,const char * s2)
{
char* p2 = (char*)s2;
char* dest1 = (char*)dest;
while (*dest1)
{
dest1++;
}
while (*dest1 = *p2)
{
dest1++;
p2++;
}
}
(4)strcmp函数介绍及模拟实现
介绍:判断两个字符串的大小,第一个字符串大于第二个字符串,则返回大于0的数字;
第一个字符串等于第二个字符串,则返回0;
第一个字符串小于第二个字符串,则返回小于0的数字;
模拟实现:
int my_strcmp(const char* s1, const char* s2)
{
while (((unsigned char*)s1 - (unsigned char*)s2) && *s2)
{
s1++;
s2++;
}
return (unsigned char*)s1 - (unsigned char*)s2;
}
(5)strncpy函数介绍及模拟实现
介绍:与strcpy相似,区别是拷贝num个字符从源字符串到目标空间。如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
char * strncpy ( char * destination, const char * source, size_t num );
模拟实现:
char* my_strncpy(char* dest, const char* src, int num)
{
char* ret = src;
for(int i= 0;i<num;i++)
{
if (*ret)
{
dest[i] = ret[i];
}
else
{
dest[i] = 0;
}
}
return dest;
}
(6)strncat函数介绍及模拟实现
介绍:与strcat相似,区别是操作num个字符。
char * strncat ( char * destination, const char * source, size_t num );
模拟实现:
char* my_strncat(const char* dest, const char * src, int num)
{
char* ret = dest;
char* p2 = src;
while (*ret)
{
ret++;
}
while (num--)
{
if (*p2)
{
*ret = *p2;
p2++;
ret++;
}
else
{
*ret = 0;
ret++;
}
}
return dest;
}
(7)strncmp函数介绍及模拟实现
介绍:与strcmp相似,区别是操作num个字符。模拟实现也相似,不再赘述。
int strncmp ( const char * str1, const char * str2, size_t num );
(8)strstr函数介绍及模拟实现
介绍:返回字符串中首次出现子串的地址。
char * strstr ( const char *, const char * );
模拟实现:也可以通过KMP算法实现
char *my_strstr(const char* str1, const char* str2 )
{
assert(str1);
assert(str2);
char *cp = (char*)str1;
char *substr = (char *)str2;
char *s1 = NULL;
if(*str2 == '\0')
return NULL;
while(*cp)
{
s1 = cp;
substr = str2;
while(*s1 && *substr && (*s1 == *substr))
{
s1++;
substr++;
}
if(*substr == '\0')
return cp;
cp++;
}
}
(9)strtok函数介绍及模拟实现
介绍:strtok()用来将字符串分割成一个个片段,第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。 strtok函数的第一个参数不为NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。strtok函数的第一个参数为NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。如果字符串中不存在更多的 标记,则返回NULL 指针。
char * strtok ( char * str, const char * sep );
(10)strerror函数介绍及模拟实现
介绍:返回错误码,所对应的错误信息。必须包含的头文件<errno.h>.
char * strerror ( int errnum );
(11)字符分类函数
包括:
iscntrl :任何控制字符;
isspace :空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v';
isdigit :十进制数字 0~9;
isxdigit :十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F;
islower :小写字母a~z;
isupper: 大写字母A~Z;
isalpha:字母a~z或A~Z;
isalnum:字母或者数字,a~z,A~Z,0~9;
ispunct: 标点符号,任何不属于数字或者字母的图形字符(可打印);
isgraph: 任何图形字符;
isprint: 任何可打印字符,包括图形字符和空白字符;
(12)memcpy函数介绍及模拟实现
介绍:与strcpy的区别是,可操作非char型指针,函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到'\0' 的时候并不会停下来。如果source和destination有任何的重叠,复制的结果都是未 定义的。
void * memcpy ( void * destination, const void * source, size_t num );
模拟实现:dest 和src转移强制类型转化为char(一个字节)才可以进行操纵,void不能操作;
void* my_memcpy(void* dest, void* src, int num)
{
assert(dest);
assert(src);
void* ret = dest;
while (num--) //num个字节
{
*(char * )dest = *(char *)src; //转移强制类型转化为char(一个字节)才可以进行操纵,void不能操作;
((char*)dest)++; //必须加括号,分清楚优先级。
((char*) src)++;
}
return ret;
}
(13)memcmp函数介绍及模拟实现
介绍:比较从ptr1和ptr2指针开始的num个字节的大小;实现方式与strcmp相似,不再赘述。
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
(14)memmove函数介绍及模拟实现
介绍:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。
void * memmove ( void * destination, const void * source, size_t num );
模拟实现:当dest 处于 src之后。应当从后向前复制,可避免不能重叠的问题,反之从前向后,这就可以对重叠内存块进行复制
void* my_memmove(void* dest, void* src, int num)
{
assert(dest);
assert(src);
void* ret = dest;
if (dest > src) //当dest 处于 src之后。应当从后向前复制,可避免不能重叠的问题,反之从前向后
{
while (num--) //num个字节,直接加上,即使最后段
{
*((char*)dest + num) = *((char*)src + num);
}
}
else
{
while (num--) //num个字节
{
*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src) ++;
}
}
return ret;
}