1.strlen
strlen函数的作用是求字符串的长度,传入字符串的首元素地址,返回该字符串的长度。
注意这里返回值的参数为size_t,即无符号整形,所以返回值ret也应该是size_t类型。而且由于对无符号整形进行加减运算会得到无符号整型,所以无法通过比较两个函数返回的strlen值进行长短比较。
//模拟实现strlen函数
size_t my_strlen(const char* arr)
{
assert(arr);//断言,保证arr不为空指针
size_t ret = 0;//使用变量进行计数
while (*arr++)//当*arr=='\0'跳出循环,否则进入循环
ret++;//长度+1
return ret;
}
2.strcpy
strcpy函数的作用是将源字符串的内容拷贝到目标空间。
注意
1.源字符串必须以\0结尾,否则拷贝会继续向后进行,直到遇到\0。
2.目标字符串必须要有足够的空间
//模拟实现strcpy函数
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);//断言
char* ret = dest;//保存目标字符串首元素地址以便返回
while (*dest++ = *src++)//进行拷贝
;
return ret;
}
3.strncpy
strncpy的作用是将源字符串中指定数量的字符拷贝到目标空间中。
但是也有需要注意的地方:
如果源字符串字符不够,会自动用\0补充。比如源字符串为"abc",转移6个字符,则会拷贝a b c \0 \0 \0到目标空间。
4.strcat
strcat函数的作用是追加一个字符串到目标字符串的结尾。传入目标字符串的首元素地址,与源字符串的首元素地址,返回目标字符串的首元素地址。
使用strcat要注意三点
1.源字符串以必须\0结尾以停止追加
2.目标字符串必须有足够空间,所以目标字符串必须指定大小
3.目标可以被修改
//模拟实现strcat函数
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);//断言
char* ret = dest;//保存目标字符串首元素地址以便返回
while (*dest)//从目标空间起始地址向后寻找\0
dest++;
while (*dest++ = *src++)//进行追加
;
return ret;
}
可以看到strcat的模拟实现与strcpy的模拟实现过程非常相似,仅仅是增加了寻找目标字符串结尾位置的步骤。
5.strncat
strncat和strcat的作用类似,但是指定了追加字符的数量。
要注意,如果源字符串没有以\0结尾,则会在追加结束后再追加上\0,且最多只追加一个\0。
6.strcmp
strcmp的作用是进行两个字符串的比较,这里比较的不是字符串的长度,而是比较字符的大小。
假设传入的两个数组为strcmp(arr1,arr2)
这里比较大小的规则为,从两个字符串的第一个字符开始,若同一位字符相同,则比较下一位,若不同,当arr1内的元素ASCII码值大于arr2内的元素,那么返回一个大于0的数,当arr2内的元素ASCII码值大于arr1内的元素,则返回一个小于0的数。若直到两个字符串结束两者都没有出现不等的情况,则返回0,表示两个字符串完全相等。
例如 字符串"abcd"与"abc"比较则返回值大于0(因为d的ASCII码值大于abc后的'\0'的ASCII码值)
"abcd"与"abcc"比较返回值大于0(因为d的ASCII码值大于c的ASCII码值)
而"abc"与"abc"比较则返回0
//模拟实现strcmp函数
int my_strcmp(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);//断言
while (*arr1 == *arr2 && *arr1)//当arr1内元素与arr2内元素相等且不为'\0'进入循环
arr1++,arr2++;//指针分别+1,指向下一个元素
return *arr1 - *arr2;//返回比较值,当两者相等时arr1 arr2都指向'\0' 相减为数字0
}
7.strncmp
将两个字符串的前n个字符以strcmp的规则进行比较
8.strtok
strtok函数将把第一个遇到的分割符改为\0,返回第一个分割好的字符串的首元素地址,传入空指针NULL则会从第一个分割符的位置向后查找下一个分割符,如果没有可检索的字符串,则返回一个空指针。
例子:被检索的字符串arr1="ab#cd@ef" 以及分割符字符串arr2="#@",要将arr1进行分割,则
第一次使用:strtok(arr1,arr2),返回值为元素a的地址 arr1="ab\0cd@ef"
第二次使用:strtok(NULL,arr2),返回值为元素c的地址 arr1="ab\0cd\0ef"
第三次使用:strtok(NULL,arr2),返回值为元素e的地址 arr1="ab\0cd\0ef"
注意在第三次使用时,虽然没有寻找到分割符,但是strtok仍对e开头的字符串进行了检索,所以并没有返回空指针,而是返回了元素e的地址。
9.strstr
strstr函数的作用是返回字符串中首次出现子串的地址,找不到则返回NULL,并且当字串为空字符串时,返回字符串的首元素地址。
模拟实现的逻辑为,首先判断arr2是否为空字符串,再进行查找。
查找过程为:每次进入循环先判断arr1是否指向\0,若指向\0则说明arr1遍历结束也没有找到字串,退出循环,返回NULL。若没有指向\0则进入循环,对以arr1为首元素的字串进行判断,若没有找到,则arr1指向下一个元素。
对以arr1为首元素的字串进行判断过程如下:初始化str1与str2指针,分别赋值为arr1与arr2,以供当元素相等时向后查找,当查找过程中str1不等str2或者任意一方指向元素为\0时退出循环,判断str2所指向元素是否为\0。若为\0,则说明找到了字串,此时返回arr1。
这里设计str1与str2的原因在于在判断时要对每一个字符为首的子字符串进行判断,若只使用arr1与arr2,当在"abbbcd"中查找"bbc"时,在arr1指向第一个b时,"bbb"并不能与"bbc"匹配,但在下一次判断时arr1直接指向了d,arr2指向了子串里的c,导致无法正常进行第二次判断,结果出错。
//模拟实现strstr函数
char* my_strstr(const char* arr1,const char* arr2)
{
assert(arr1 && arr2);//断言
if (!*arr2)//如果arr2为空字符串,返回arr1的首元素地址
return (char*)arr1;
while (*arr1)//arr1不指向\0时进入循环
{
char* str1 = (char*)arr1;//定义两个指针进行字符串的匹配
char* str2 = (char*)arr2;
while (*str1 && *str1 == *str2)//当两者不为\0且相等时进入循环
{
str1++;//指针分别指向下一个元素
str2++;
}
if (!*str2)//若结束循环后str2指向\0则说明找到字串
return (char*)arr1;
arr1++;//匹配失败时arr1指向下一个字符
}
return NULL;//找不到则返回NULL
}
10.memcpy
memcpy函数的作用是将源空间的数据以字节为单位拷贝到目标空间。并且返回目标空间的地址。
//模拟实现memcpy函数
void* my_memcpy(void* dest, const void* src, size_t count)
{
assert(dest && src);//断言
void* ret = dest;//保留目标空间地址以便返回
while (count--)//拷贝count个字节的数据
{
*(char*)dest = *(char*)src;//拷贝
dest = (char*)dest + 1;//指针指向下一个字节
src = (char*)src + 1;
}
return ret;
}
11.memmove
memmove和memcpy的区别是当内存放生局部重叠时,memmove保证结果正确,memcpy不保证拷贝结果正确。memmove会检测拷贝的进行方向并且以不会出错的方向进行拷贝。
为什么说memcpy可能发生错误呢?比如将数组{1,2,3,4,5,6,7,8,9,10}内的1 2 3 4拷贝到 3 4 5 6的位置,按逻辑,结果应该是1,2,1,2,3,4,7,8,9,10
但是从左向右拷贝
拷贝一个整形后 第一个整形1拷贝到第三个整形的位置后数组变为{1,2,1,4,5,6,7,8,9,10}
拷贝两个整形后 第二个整形2拷贝到第四个整形的位置后数组变为{1,2,1,2,5,6,7,8,9,10}
拷贝三个整形后 第三个整形1拷贝到第五个整形的位置后数组变为{1,2,1,2,1,6,7,8,9,10}
拷贝四个整形后 第三个整形2拷贝到第六个整形的位置后数组变为{1,2,1,2,1,2,7,8,9,10}
这与我们设想的结果不同,因为我们拷贝的数据将原有位置的数据覆盖,导致无法正常进行拷贝。但是memmove在拷贝前会进行方向判断,以保证拷贝的结果正确。在上面的例子中,从右向左拷贝,则
拷贝一个整形后 数组变为{1,2,3,4,5,4,7,8,9,10}
拷贝两个整形后 数组变为{1,2,3,4,3,4,7,8,9,10}
拷贝三个整形后 数组变为{1,2,3,2,3,4,7,8,9,10}
拷贝四个整形后 数组变为{1,2,1,2,3,4,7,8,9,10}
也就正常完成了我们的拷贝
//模拟实现memmove函数
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(dest && src);
void* ret = dest;
if (dest > src)//判断目标空间与源空间的关系
{//从右向左拷贝
dest = (char*)dest + count - 1;
src = (char*)src + count - 1;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest - 1;
src = (char*)src - 1;
}
}
else
{//从左向右拷贝
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}