学习字符串一段时间,整理下字符串函数的实现,并加入一些注释。
strlen -- 计算字符串的长度
/*返回值:目标串的长度
*参数:str为欲计算长度的字符串
*/
size_t strlen(const char* str)
{
size_t length = 0;
while(*str++) //注:*p++相当于(*p)++
++length;
return length;
}
稍加改进如下:
size_t strlen(const char* str)
{
const char* cp = str;
while(*cp++) ;
return (cp - str -1);
}
不借助中间变量:
(标准写法)
int strlen(char* str)
{
if(str[0] == '\0')
return 0;
else
return ( strlen( (char*) (&str[0] + 1) ) + 1 ); //运用递归
}
(简略写法)
int strlen(char* str)
{
if(*str == '\0')
return 0;
else
return ( (strlen(str + 1) ) + 1);
}
但是如果给此函数传入一个NULL的话,程序会崩溃。所以,用以下的做法加强函数的容错性和健壮性:
int strlen(const char* str)
{
int len = 0;
assert(str != NULL); //使用断言要include头文件assert.h,
assert表示理论可能但实际不可能发生的事
while(*str++)
len++;
return len;
}
strcpy -- 字符串拷贝
/*返回值:目标串的地址
*参数:dst为目标字符串,src为原字符串
*/
char* strcpy(char* dst, const char* src)
{
char* r = dst;
assert( (dst != NULL) && (src != NULL) );
while( (*dst++ = *src++) != '\0' ); //
赋值表达式返回左操作数,所以在赋值NULL后,循环停止
return r;
}
1. 为什么要返回char*?
--
为了实现链式表达式,例如 int length = strlen( strcpy(dst, "hello world" ) );
2. 假如考虑dst和src内存重叠的情况,strcpy该怎么实现?
解决内存重叠,用memmove,原型是void* memmove(void* dst, const void* src, size_t count)。
3. 这个函数不会自己判断原字符串是否比目标空间大,必须要程序员自己检查,否则很容易造成拷贝越界,如:
char* a = "hello world";
char c[5];
strcpy(c, a);
//输出为c = "hello world",数组c只有5个字节的空间,但是经过strcpy后a的剩余字符也拷贝过去了,如果c后面是系统程
序空间,那就要出问题了。
strncpy -- strcpy 的改进版本,拷贝规定长度的字符串
/*返回值:目标串的地址
*参数:dst为目标字符串,src为原字符串, len为复制字符串的长度,即使未遇到原串的'\0',如果已经复制了len个字符,复制同样会终止。
*/
char* strcpy(char* dst, const char* src, int len)
{
assert(dst != NULL && src != NULL);
char* r = dst;
int i = 0;
while(i++ < len && *dst++ = *src++);
if(*dst != '\0')
*dst = '\0';
return r;
}
多了一个拷贝长度的参数。需要注意的是长度参数应该为目的空间的大小,并且这个函数不会自己附加字符串结束符'\0',要自己加。看下面的例子:
char* a = "hello world";
char* b = "123456789";
char c[5];
strncpy(c, b, strlen(b) ) = 123456789 //拷贝长度不对,还是越界
strncpy(c, b, strlen(b) ) = 123456789 //拷贝长度不对,还是越界
strncpy(c, a, sizeof(c) ) = hello6789
//拷贝长度正确,但是因为拷贝长度内不包括'\0',所以输出的时候还是会把原本的空间内容输
出,直到遇到一个结束符'\0'。
所以正确的做法应该是: strncpy(c, a, sizeof(c)-1); c[4] = '\0';
//输出为c=hell
memcpy -- 类似strncpy
区别:
(1)strncpy只能复制字符串,但memcpy对类型没有要求。
(2)strncpy有两个终止条件,memcpy只有一个终止条件,那就是复制n个字节。(n是memcpy的第三个参数)
(3)要特别注意目的地址和源地址重合的问题,拷贝前要加以判断。
(4)实现这个函数时一般要把原来的指针类型转换成char*,这样每次移动都是一个字节。
strncpy是把Num个字符从src复制到dest,但是如果遇到src字符结尾,那么复制提前结束,后面没有复制完的字符,不予以处理,当然dest,src地址不能重叠。
memcpy也是把Num个字符从src复制到dest,但是它是内存复制,完整的复制Num个字节,不会因为遇到'\0'而结束。
函数实现:
void* memcpy(void* dst, void* src, unsigned int count)
{
assert( dst != NULL && src != NULL);
if(dst == src)
return src;
char* d = (char*)dst; //在函数里面生成临时指针,这样不会改变原始指针
char* s = (char*)s;
while(count-- > 0)
*d++ = *s++;
return dst;
}
strcat -- 字符串拼接
/*功能:把str2所指字符串添加到str1结尾处(覆盖str1结尾处的'\0')并添加'\0'
*返回值:拼接后的字符串首地址
*参数:str1为原字符串,str2为要拼接的字符串
*/
char* strcat(char* str1, char* str2)
{
assert(str1 != NULL || str2 != NULL);
char* pt = str1;
//while(*str1++ != '\0'); //不能这样遍历str1!str1移到了的下一位,导致后面拼接后再打印的字符串也只能读出str1,读到'\0'处就结
char* pt = str1;
//while(*str1++ != '\0'); //不能这样遍历str1!str1移到了的下一位,导致后面拼接后再打印的字符串也只能读出str1,读到'\0'处就结
//束了。
*和++的优先级是一样的,当优先级一样时,程序按自左至右的顺序执行,所以当*str1='\0'时,str仍
//然要往下移一位,即
str1指向满足条件后的下一个字节。
while(*str1 != '\0') str1++; //正确做法!str1移到'\0'处就停止了。
while((*str1++ = *str2++) != '\0'); //本句的计算顺序: 1. *str1=*str2; 2. 判断整个表达式的值是否为真,即*str1!='\0'; 3. 满足,
while((*str1++ = *str2++) != '\0'); //本句的计算顺序: 1. *str1=*str2; 2. 判断整个表达式的值是否为真,即*str1!='\0'; 3. 满足,
//
则继续循环,否则终止,不论循环继续与否,接下来要执行str1++;str2++; str1和str2谁先自增
//在这里是无关紧要的。
//while(*str2 != '\0') *str1++ = *str2++;
//*str1 = '\0'; //这两句与上一句等效
return pt;
//*str1 = '\0'; //这两句与上一句等效
return pt;
}
注:这里返回char*,同样是为了实现链式表达式。
strcmp -- 比较两个字符串
/*返回值:0:s1 == s2; 1:s1 > s2;-1:s1 < s2.
*参数:s1, s2为要比较的字符串
*/
int strcmp(const char* s1, const char* s2)
{
int ret = 0;
while( !( ret = *(unsigned char*)s1 - *(unsigned char*)s2 ) && *s2 )
//直到s1和s2当前数值不相等且s2不为\0时退出while
{
++s1;
++s2;
}
if( ret < 0 )
ret = -1;
else if( ret > 0 )
ret = 1;
return ret;
}
strchr -- 查找字符串中首次出现字符ch的位置
/*返回值:如果字符串中存在字符ch,返回首次出现ch的位置指针,否则返回NULL。
*参数:s1, s2为要比较的字符串
*/
char* strchr(const char* str, int ch)
{
while(*str && *str != (char)ch)
str++;
if(*str == (char)ch)
return (char*)str;
return NULL;
}
strstr -- 查找字符串中第一次包含另一个字符串的位置
char* strstr(const char* s1, const char* s2)
{
if(*s1 == 0)
{
if(*s2)
return NULL;
return (char*)s1;
}
while(*s1)
{
int i = 0;
while(1)
{
if(s2[i] == 0)
return (char*)s1;
if(s1[i] != s2[i])
break;
i++;
}
s1++;
}
return NULL;
}