字符操作库函数
-
strlen
strlen判断结束的标志为找到字符串中的'\0',也就是说如果字符串中间出现'\0'将会导致strlen停止 即strlen的返回值是'\0'前所出现的字符个数 然而此时字符串长度并非真实长度,但一般很少出现字符串中间会有‘\0’
size_t strlen(const char* str)
模拟实现my_strlen:
方式1:直接计数 遍历整个字符串 直至找到“\0”为止 否则计算器实现加一操作
int my_strlen(const char* str)
{
int count=0;
while(*str)
{
count++;
str++;
}
return count;
}
方式2:不引入整型计数变量,利用递归实现计数
int my_strlen(const char* str)
{
if(*str=='\0')
{
return 0;
}
return 1+my_strlen(str+1);
}
方式3:指针方式实现计数,遍历不为‘\0’则向右移动指针,直至指针p移动至‘\0’前一位置,p-str即为中间不为'\0'的字符个数
int my_strlen(const char* str)
{
char* p=(char*)str;
while(*p!='\0')
{
p++;
}
return p-str;
}
-
strcpy
源字符串必须以'\0'结束,同时,目标空间必须足够大,以免源字符串拷贝空间不足,通过实际代码调试查看内存空间 注意到,拷贝过程中会将源字符串结尾的‘\0’拷贝至目标地址空间中
char* strcpy(char* destination,const char* source)
my_strcpy模拟实现:
当判断源字符串不为空,以及目标空间存在的情况下,实现从源字符串到目标空间的逐个字符拷贝,直至源字符串遇到'\0'结束拷贝
注意:while(*dest++=*src++); 当src遇到'\0',赋值给dest后,表达式结果为0,为false,则循环结束,暂停拷贝
char* my_strcpy(char* dest,const char* src)
{
char* ret=dest;
assert(dest!=NULL)
assert(src!=NULL)
while(*dest++=*src++);
return ret;
}
-
strcat
char* strcat(char* destination,const char* source)
strcat模拟实现,可以在strcpy基础上进行,即在目标空间末尾(不包括‘\0’)进行一次strcpy即可实现,具体参见代码
char* my_strcat(char* dest,const char* src)
{
char* ret=dest;
assert(dest!=NULL)
assert(src!=NULL)
while(*dest!=NULL)
{
dest++;
}
while(*dest++=*src++);
return ret;
}
考虑:strcat自己给自己追加会发生什么?
自己给自己追加的过程中,源字符串末尾的‘\0’被覆盖,导致while循环进入死循环,无法再找到‘\0’停止,此时starcat将一直拷贝连接 直至目标空间溢出报错
-
strcmp
strcmp实现字符串比较,通过对应字符ASCll码比较大小
int strcmp(const char* src1,const char* src2)
如果第一个字符串大于第二个字符串,返回大于0的数
如果第一个字符串等于第二个字符串,返回等于0的数
如果第一个字符串小于第二个字符串,返回小于0的数
strcmp的模拟实现:
int my_strcmp(const char* src1,const char* src2)
{
int ret=0;
assert(src1!=NULL);
assert(src2!=NULL);
while(!(ret=*(unsigned char*)src1-*(unsigned char*)src2)&&*src2)//相等字符则后移比较,直至遇到‘\0’
{
src1++;
src2++;
}
if(ret<0)
ret=-1;
else if(ret>0)
ret=1;
return ret;
}
-
strstr
strstr实现子串查找,返回子串首元素地址
char* strstr(const char*,const char*)
模拟实现my_strstr():主要难点在于一个字符一个字符逐一匹配,需要一个指针记录起始位置,每次匹配失败需要退回起始位置的下一位置处开始在次匹配
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1,const char* str2)
{
assert(str1);
assert(str2);
const char* s1 = str1;
const char* s2 = str2;
const char* cp = str1;//该指针记录每一次进行匹配的起始位置
if (*str2 == '\0')
{
return (char*)str1;
}
while (*cp)
{
//一次匹配查找的过程
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)//逐个匹配
{
s1++;
s2++;
}
if (*s2 == '\0')//只有s2走完才能说明找到真正的子串 返回起始查找位置cp s1走完还没有找到匹配的子串,则返回null
{
return (char*)cp;
}
cp++;
}
return NULL;
}
int main()
{
char* str1 = "abcdef";
char* str2 = "cde";
char* ret = my_strstr(str1,str2);
printf("%s",ret);
return 0;
}
kmp算法实现字符串匹配详细可参考http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
内存操作库函数
-
memcpy
函数从source拷贝nums个字节到destination中,此时拷贝字节,遇到'\0'并不会停止
void* memcpy(void* destination,const void* source,size_t nums)
memcpy拷贝过程中 源内存块和目标内存块不可以重叠
由于类型不确定,为void*类型,为了实现一字节一字节拷贝,需要强制类型转换为char*类型具体代码如下:
void* my_memcpy(void* dest,const void* str,size_t count)
{
void* ret = dest;
//一字节一字节拷贝,一字节一字节后移
while (count--)
{
*(char*)dest = *(char*)str;
dest = (char*)dest + 1;
str = (char*)str + 1;
}
return ret;
}
-
memmove
memmove与memcpy功能基本类似,都是实现内存块数据拷贝,memmove可以实现重叠内存拷贝
重叠内存拷贝 不同的拷贝方向会导致尚未拷贝的内存空间 cd被覆盖为bb 导致后续拷贝也只拷贝了bb 如图所示:
解决方案,从source后面开始拷贝,从后向前拷贝,则不会被覆盖,但情况不一定,如果红色框为dest,绿色框为source,从后向前拷贝又会被覆盖
考虑是否存在一个分界条件,使得分情况处理即可
如果拷贝的目的地址在源地址前面 则源地址从前向后拷贝到目的地址
如果拷贝的目的地址在源地址后面 则源地址从后向前拷贝到目的地址
void* my_memmove(void* dest, const void* str, size_t count)
{
//避免拷贝重叠 需要分情况讨论
void* ret = dest;
if (dest < str)//如果拷贝的目的地址在源地址前面 则源地址从前向后拷贝到目的地址
{
while (count--)
{
//依然遵循一字节一字节拷贝
*(char*)dest = *(char*)str;
dest = (char*)dest + 1;
str = (char*)str + 1;
}
}
else//如果拷贝的目的地址在源地址后面 则源地址从后向前拷贝到目的地址
{
while (count--)
{
*((char*)dest + count) = *((char*)str + count);
}
}
return ret;
}