这里写目录标题
前言
字符和字符串的处理几乎是每个初学者都会使用的程序,今天我们来了解一下一些关于处理字符和字符串的库函数,知道库函数的程序原理,和模仿实现一下部分函数。
函数的介绍
1. strlen函数
size_t strlen ( const char * str );
strlen函数是测量字符串的长度,以\0
为结束标志,计算\0
之前共有多少个字符个数(这里字符个数不包括\0
)并将其返回。
1.1 strlen函数作为一个处理字符串的函数,它的所以它的参数是一个字符指针。
1.2 strlen函数的目的是测量字符串的长度,这里加一个const修饰参数,防止字符串被修改。
1.3 strlen函数的返回值是size_t,是因为一个字符串的长度不可能为负数。
1.4 需要注意的是字符串必须有\0
结尾,否则计算出来的值会是大于字符串长度的一个随机值。
2. strcpy函数
char * strcpy ( char * destination, const char * source );
strcpy函数是将source中指向的字符串复制到destination指向的数组中。
2.1 strcpy函数需要两个参数,一个是指向复制内容目的数组(destination),另一个是我们需要的字符串(source)。
2.2 调用strcpy函数时我们需要改变的是destination指向的数组,所以只需在另一参数上加以const修饰。
2.3 调用strcpy函数时,字符串必须有\0结尾,并且数组必须足够大,能够放下字符串。
2.4 strcpy函数返回的是目的数组的首元素地址。
3. strcat函数
char * strcat ( char * destination, const char * source );
strcat函数是将source中指向的字符串复制并追加到destination指向的数组中。
3.1 strcat函数需要两个参数,一个是指向复制内容目的数组(destination),另一个是我们需要的字符串(source)。
3.2 调用strcat函数时我们需要改变的是destination指向的数组,所以只需在另一参数上加以const修饰。
3.3 调用strcat函数时,字符串必须有\0
结尾,并且数组必须足够大,能够放下字符串。
3.4 strcat函数返回的是目的数组的首元素地址。
3.5 值得注意的是追加的时候是在\0
的位置上开始追加而不是\0
之后开始追加。
4. strcmp函数
int strcmp ( const char * str1, const char * str2 );
strcmp函数用来比较两个字符串是否相等,并不是比较两个字符串代数和的大小,而是比较两个指针指向的字符的大小。
(1)字符串一大于字符串二时返回一个大于0的数
(2)字符串一等于字符串二时返回0
(3)字符串一小于字符串二时返回一个小于0的数
4.1 strcmp函数需要两个字符串的首地址作为函数参数。
4.2 strcmp函数只是用来比较两个字符串是否相等,并不希望改变字符串,所以为两个参数加上const修饰。
4.3 这个函数需要两个字符串均有\0
结尾
5. strncpy函数
char * strncpy ( char * destination, const char * source, size_t num );
strncpy函数与strcpy函数功能相似,但不同的是strncpy函数能够控制复制的字符个数。
strncpy函数会将source指向的字符串拷贝num个字符到目标数组。
6.strncat函数
char * strncat ( char * destination, const char * source, size_t num );
strncat函数与strcat函数功能相似,但不同的是strncat函数能够控制追加的字符个数。
strncat函数会将source指向的字符串追加num个字符到目标数组。
7. strncmp函数
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp函数与strcmp函数功能相似,但不同的是strncmp函数能够控制对比的字符个数。
strncmp函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下对,直到字符不同,直到达到终止的空字符,或者直到两个字符串中的 num 字符匹配。
8. strstr函数
const char * strstr ( const char * str1, const char * str2 );
strstr函数的作用是找子字符串
8.1 函数返回在str2指向的字符串第一次找到str1字符串的地址。若找不到,则返回NULL
。
8.2 函数将两个字符串匹配的时候到\0
终止,但匹配时不包括\0
。
9. strtok函数
char * strtok ( char * str, const char * delimiters );
9.1 delimiters是一个字符串,它是分割字符的集合。
9.2 第一次调用strtok函数时参数需要一个字符串,而后续调用时需要一个空指针(NULL)
。
9.3 strtok函数会找到下一个分割符并作标记,并在这个标记置换成\0
结尾。例如这里:
#include <stdio.h>
#include <string.h>
int main()
{
char* p = ":/.";
char arr[] = "http://t.csdn.cn/h4GfL";
char* ret = NULL;
ret = strtok(arr, p);
printf("%s\n", ret);
//调用一次函数字符串变为http\0//t.csdn.cn/h4GfL
//并且strtok函数会记得h的地址,后面的以此类推
ret = strtok(NULL, p);
printf("%s\n", ret);
//调用两次函数字符串变为http\0\0/t.csdn.cn/h4GfL
ret = strtok(NULL, p);
printf("%s\n", ret);
//调用三次函数字符串变为http\0\0\0t.csdn.cn/h4GfL
ret = strtok(NULL, p);
printf("%s\n", ret);
//调用四次函数字符串变为http\0\0\0t\0csdn.cn/h4GfL
ret = strtok(NULL, p);
printf("%s\n", ret);
//调用五次函数字符串变为http\0\0\0t\0csdn\0cn/h4GfL
ret = strtok(NULL, p);
printf("%s\n", ret);
//调用六次函数字符串变为http\0\0\0t\0csdn\0cn\0h4GfL
return 0;
}
//代码可化简效果一样,可以方便后续改变字符串而不增加上面代码的重复。
#include <stdio.h>
#include <string.h>
int main()
{
char* p = ":/.";
char arr[] = "http://t.csdn.cn/h4GfL";
char* ret = NULL;
for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
return 0;
}
9.4 strtok函数具有记忆功能,它能够将分隔符变为\0
,并且记住分隔符的地址。
9.5 strtok函数第一个参数不为NULL
,函数找str中的第一个分隔符,并保存它在字符串中的位置。
9.6 strtok函数第一次参数为NULL
,函数则在同样的字符串中被strtok函数保存的位置,找到下一个分隔符的位置。
9.7 若字符串中没有更多的分割符,则函数会返回NULL
。
9.8 值得注意的是strtok函数会真的改变字符串的内容,所以我们使用strtok函数的时候一般会临时拷贝一份字符串。
10. strerror函数
char * strerror ( int errnum );
strerror函数能够将errnum中的值转换为错误信息。
这里有个功能相似的函数:perror函数
void perror ( const char * str );
perror函数也能够将erron中的值转换为错误信息,但是相比之下多了一个功能就是能够直接打印错误信息。
所以我们按照是否打印来选择使用哪个函数。
11. memcpy函数
void * memcpy ( void * destination, const void * source, size_t num );
memcpy函数能够将源空间内的num个字节直接复制到目标空间内。
11.1 memcpy函数与strncpy函数的功能类似,但mencpy函数不止局限与字符串。我们选择时应该选择专攻的,例如使用复制字符串的时候用strcpy函数,其他整形数组、浮点型数组使用strncpy函数。
11.2 该函数不检查源中的任何终止空字符(\0)
, 所以它总是准确地复制字节数。
11.3 若两个内存块重叠时,因为编译器的原因,结果并不能确定。
12. memmove函数
void * memmove ( void * destination, const void * source, size_t num );
memmove函数的功能与memcpy函数的功能一致,但是两个内存块重叠时,memmove函数并不会受影响,也可以说memmove函数包含了所有memcpy函数的功能但少了重叠的问题。
所以在两个内存块重叠的时候我们尽量使用memmove函数,也可以只使用memmove函数。
13. memcmp函数
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
13.1 memcmp函数与strcmp函数相似但不限制比较类型。
13.2 memcmp函数会将ptr1后面的num个字节与ptr2的第一个字节进行比较,若相同则ptr2向后移动继续与ptr1的后面的字节比较。若全部相等则返回0,否则会按ptr1与ptr2的大小关系返回一个不为零的数。
13.3 注意该函数不检查源中的任何终止空字符(\0)
部分库函数的模拟实现
1. strlen函数的模拟实现
方法一:计数器方式
size_t my_strlen1(const char* str)
{
int count = 0;
while (*str)
{
str++;
count++;
}
return count;
}
方法二:递归方式(不创建临时变量)
size_t my_strlen2(const char* str)
{
if (*str != '\0')
return 1 + my_strlen2(str + 1);
else
return 0;
}
方法三:指针-指针
size_t my_strlen3(char* str)
{
char* ret = str;
while (*str != '\0')
{
str++;;
}
return str - ret;
}
int main()
{
char arr[] = "abcdef";
//int ret = my_strlen1(arr);
//int ret = my_strlen2(arr);
int ret = my_strlen3(arr);
printf("%d", ret);
return 0;
}
//a b c d e f \0
//注意指针-指针计算的是\0之前的元素个数
2. strcpy函数的模拟实现
#include <stdio.h>
#include <assert.h>
//const修饰防止src被修改
char* my_strcpy(char* dest,const char* src)
{
char* ret = dest;
//断言,防止dest 与 src 为空指针 ,若为空指针则报错停止程序
assert(dest && src);
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[10] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
3. strncpy函数的模拟实现
char* my_strncpy(char* dest,const char* src , size_t num)
{ //注意num的单位是字节
char* ret = dest;
assert(dest && src);
while (num--)
{
*dest++ = *src++;
}
return ret;
}
4. strcat函数的模拟实现
#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
//找到dest指向'\0'的位置
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[10] = "ghi";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
//abcdefghi
return 0;
}
5. strncat函数的模拟实现
char* my_strncat(char* dest, const char* src , size_t num)
{ //注意num的单位是字节
char* ret = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
while (num--)
{
*dest++ = *src++;
}
return ret;
}
6. strstr函数的模拟实现
#include <stdio.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* p1 = NULL;
char* p2 = NULL;
char* mark =(char*) str1;
//这里str1的类型是const char* ,mark的类型是char* 将str1赋给mark,类型由不可变变为可变
//变的不安全,编译器会报警告,所以将str1强制类型转换为char*,下面的p2同理
while (1)
{
p1 = mark;
p2 = (char*)str2;
//这里*p1 和 *p2 为'\0'就没有继续循环的必要了
//如果*p1为'\0',p1指向的字符串已经结束,要么*p2为'\0'找到了,要么*p2不为'\0'找不到
//如果*p2为’\0'则p2指向的字符串已经遍历完了,p1指向的字符串存在p2指向的字符串
while (*p1!= '\0' && *p2 != '\0' && *p1 == *p2)
{
p1++;
p2++;
}
if (*p2 == '\0')
{
return mark;
}
mark++;
}
return NULL;
}
int main()
{
char arr1[10] = "abcdef";
char arr2[10] = "bcd";
char* ret = my_strstr(arr1, arr2);
printf("%s\n", ret);
return 0;
}
7. strcmp函数的模拟实现
#include <stdio.h>
#include <assert.h>
//const修饰防止字符串被修改
int my_strcmp(const char* dest, const char* src)
{
assert(dest && src);
//*dest == *src && *dest == '\0' 则*dest == *src == '\0'
//两字符串都遍历完了且相同
while (*dest == *src)
{
if (*dest == '\0')
return 0;
dest++;
src++;
}
return *dest - *src;
}
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abcdef";
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
8.strncmp函数的模拟实现
#include <stdio.h>
#include <assert.h>
//const修饰防止字符串被修改
int my_strncmp(const char* dest, const char* src , size_t num)
{ //注意num的单位是字节
assert(dest && src);
while (*dest == *src && num--)
{
dest++;
src++;
}
return *dest - *src;
}
9. memcpy函数的模拟实现
//写这个代码的人并不知道使用者用这个函数处理什么样的类型
//所以用void*,因为void*可以接受任意类型的指针
void* my_memcpy(void* dest,const void* src, size_t num)
{ //这里注意num的单位是字节
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
10. memmove函数的模拟实现
memmove函数与memcpy函数功能基本相同,但有了处理两块内存块重叠的能力,所以在模拟memmvoe函数的时候就要分情况,到底是从前往后覆盖,还是从后往前覆盖。
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest > src)
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
else
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
总结
到这里部分处理字符和字符串的库函数就已经讲述完毕了。
希望我的文章能够对你一定的帮助!!