目录
本章重点
本文重点介绍字符和字符串的库函数得使用和注意事项
深度解析库函数,并实现相关的库函数
一、strlen函数
函数原型
size_t strlen ( const char * str );
函数介绍
- strlen函数是用于计算字符串长度的
- 字符串以"\0"作为结束标志,此函数就是计算"\0"之前的字符个数
- 返回值为 size_t ,size_t原型为 typedef unsigned int size_t 是无符号整型的重命名,因为strlen函数是计算字符串长度,不可能为负数
- 参数用const char* 的指针接收,const为不可修改增加了函数的安全性
函数模拟实现
下述展示三种strlen函数的模拟实现,三种不同的思路
方法一:计算头尾指针的距离从而得到字符串的长度
size_t my_strlen(const char* arr)
{
assert(arr != NULL);//断言函数,用于判断函数是否为空
const char* ret = arr;//将字符数组首地址赋值给ret指针变量
while (*ret)//当ret解引用不为\0时循环继续
{
ret++;//指针向后偏移
}
return ret - arr;//计算出指针的偏移量从而得到字符串的长度
}
方法二 :利用一个标记,当数组内容不为\0时标记加一,从而得到字符串长度
size_t my_strlen1(const char* arr)
{
assert(arr != NULL);
int sum = 0;
while (*arr)
{
arr++;
sum++;
}
return sum;
}
方法三: 递归实现,当数组内容不为\0时递归调用
size_t my_strlen2(const char* arr)
{
assert(arr != NULL);
if (*arr)
return 1 + my_strlen2(arr+1);
else
return 0;
}
二、strcpy函数
char* strcpy(char * destination, const char * source );
函数介绍
- strcpy是实现字符串拷贝的函数
- destination是目标数组,source是用于拷贝的数组,即将source数组中的内容拷贝到destination中
- source字符串必须以 '\0' 结束。
- 会将source字符串中的 '\0' 拷贝到目标空间。
- destination空间必须足够大,以确保能存放source字符串。
- destination空间必须可变。
函数模拟实现
char* my_strcpy(char* destination, const char* source)
{
assert(destination && source);
char* ret = destination;//用一个字符指针记录des数组的起始地址
while (*destination = *source)//当source内容为\0时循环结束
{
destination++;
source++;
}
return ret;//返回des起始地址
}
三、strcat函数
函数原型
char * strcat ( char * destination, const char * source );
函数介绍
- strcat是字符串追加函数
- 将source的内容追加到destination中
- source字符串必须以 '\0' 结束。
- destination空间必须有足够的大,能容纳下源字符串的内容。
- destination空间必须可修改。
函数模拟实现
char* my_strcat(char* arr1, const char* arr2)
{
assert(arr1 && arr2);
char* ret = arr1;//使用一个字符指针记录目标数组的首地址
while (*arr1)//当目标函数内容为\0时,开始追加
{
arr1++;
}
while (*arr1 = *arr2)//开始追加字符串
{
arr1++;
arr2++;
}
return ret;
}
四、strcmp函数
函数原型
int strcmp ( const char * str1, const char * str2 );
函数介绍
- strcmp函数时比较两个字符串的大小
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
函数模拟实现
int my_strcmp(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);
while (*arr1 == *arr2)//通过ascll码值依次比较每个字符的大小
{
//当字符数组内容为\0时还没有比较出大小则表示两字符串相等
if (*arr1 == '\0')
return 0;//相等则返回0
arr1++;
arr2++;
}
return *arr1 - *arr2;//不相同等返回差值
}
五、strncat函数
函数原型
char * strncat ( char * destination, const char * source, size_t num );
函数介绍
- 只是在原有的strcat的基础上增加了一个size_t num
- 表示只追加num个字符
函数模拟实现
char* my_strncat(char* arr1, const char* arr2, size_t num)
{
assert(arr1 && arr2);
char* ret = arr1;
while (*arr1)
{
arr1++;
}
while (num--)//只追加num个字符
{
*arr1 = *arr2;
arr1++;
arr2++;
}
return ret;
}
六、strncpy函数
函数原型
char * strncpy ( char * destination, const char * source, size_t num );
函数介绍
- 在原有strncpy上增加了一个size_t num
- 表示只复制num个字符
函数模拟实现
char* my_strncpy(char* arr1, const char* arr2, size_t num)
{
assert(arr1 && arr2);
char* ret = arr1;
while (num--)//控制复制的字符数
{
*arr1 = *arr2;
arr1++;
arr2++;
}
return ret;
}
七、strncmp函数
函数原型
int strncmp ( const char * str1, const char * str2, size_t num );
函数介绍
- 字符串比较函数,只比较num个字符
- 相较于strcmp只多了一个num
函数模拟实现
int my_strncmp(const char* arr1, const char* arr2 ,size_t num)
{
assert(arr1 && arr2);
while (num--)
{
if (*arr1 == *arr2)//字符相等时指针后移
{
arr1++;
arr2++;
}
else
{
return *arr1 - *arr2;//只要有不相等时就返回两个字符的差
}
}
return 0;//循环结束任然相等返回0
}
八、strstr函数
函数原型
char * strstr ( const char *str1, const char * str2);
函数介绍
- strstr函数是在str1字符串中查找str2字符串是否为str1的子串
函数模拟实现
char* my_strstr(const char* arr1, const char* arr2)
{
const char* s1 = arr1;//利用s1指针记录被查询数组的地址
const char* s2 = arr2;//s2记录查询数组的地址
const char* p = arr1;//p指针为被查询数组的偏移量
while (*s1)
{
s1 = p;//从arr1第一个字符开始对比,不是字串就偏移一位
s2 = arr2;
while (*s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')//如果查询的数组为\0了那就证明arr2是arr1的子串
{
return p;
}
p++;
}
return NULL;
}
九、memcpy函数
函数原型
void * memcpy ( void * destination, const void * source, size_t num );
函数介绍
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的
- 可以传任意参数,不局限于字符数组
- 这里的num是字节数,例如传整型数组,一个整型是4字节,10个整型num的值为40
函数模拟实现
void* my_memcpy(void* arr1, const void* arr2,size_t num)
{
void* ret = arr1;
assert(arr1 && arr2);
while (num--)//num为字节数,这样就保证了能够一个字节一个字节的交换
{
*(char*)arr1 = *(char*)arr2;//将传过来的地址强制类型转换为字符型
//一个字节一个字节的复制
arr1 = (char*)arr1 + 1;//强转为char*型这样指针偏移量就为1
arr2 = (char*)arr2 + 1;
}
return ret;
}
十、memmove函数
函数原型
void * memmove ( void * destination, const void * source, size_t num );
函数介绍
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理
函数模拟实现
重叠情况一:当source指针在destination指针后面时,直接数字从前向后复制移动即可
重叠情况2:当source指针在destination指针前面时,直接数字从后向前复制移动即可
void* my_memmove(void* arr1, const void* arr2, size_t num)
{
void* ret = arr1;
assert(arr1 && arr2);
if (arr1 <= arr2)//当目标函数指针在源函数左边时
{
while (num--)//从左向右复制
{
*(char*)arr1 = *(char*)arr2;
arr1 = (char*)arr1 + 1;
arr2 = (char*)arr2 + 1;
}
}
else//目标函数在源函数右边时
{
while (num--)//从右向左依次复制
{
*((char*)arr1 + num) = *((char*)arr2 + num);
}
}
return ret;
}
总结
万丈高楼平地起,没有基础就没有后面的高楼,即使这些库函数并没有什么很难的算法,但是也是需要我们去学习的
这里总结了10个常用库函数的使用以及使用的注意事项,同时也给出了这些库函数的模拟实现
这里肯定还是存在问题的,希望看到的诸君不吝赐教!