strtok
上图是关于strtok
库函数的介绍,很多小伙伴一看就头疼了,下面让我来给大家讲解一下这个库函数的作用。这个库函数的作用是分割字符串,首先我们来看函数的声明
char * strtok ( char * str, const char * delimiters );
1.delimiters参数是个字符串,定义了用作分割符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记
6.如果字符串中不存在更多的标记,则返回 NULL 指针
我们在代码中使用一下这个库函数
int main()
{
char arr[] = "abcd.efg@abcd";
const char delimiters[] = ".@";
char* str = NULL;
char arr2[30] = { 0 };
strcpy(arr2, arr);
for (str = strtok(arr2, delimiters); str != NULL; str = strtok(NULL, delimiters))
{
printf("%s\n", str);
}
return 0;
}
我们用一张图来让大家明白strtok
是如何实现的
通过上述过程我们就实现了分割字符串的效果,这就是strtok
库函数的作用。
strerror
我们在写代码时经常会报错,错误会有一个错误码,编译器会将错误码保存在错误码遍历中,C语言提供的错误码变量为errno,而库函数strerror
的作用就是将错误码翻译成错误信息 。
我们可以在代码中演示一下,strerror
的使用场景
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
return 0;
}
我们还可以使用perror
,同样可以打印错误信息
一些字符类库函数
函数 如果符合下面条件就返回真
iscntrl
任何控制字符
isspace
空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit
十进制数字 0~9
isxdigit
十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
islower
小写字母a~z
isupper
大写字母A~Z
isalpha
字母a~ z或A~Z
isalnum
字母或者数字,a~ z,A~ Z,0~9
ispunct
标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph
任何图形字符
isprint
任何可打印字符,包括图形字符和空白字符
tolower
将大写字符转换成小写字符
toupper
将小写字符转换成大写字符
我们可以在编译器中简单使用几个这样的库函数
int main()
{
char ch[] = "Test String";
int i = 0;
char c = 0;
while (ch[i])
{
c = ch[i];
if (isupper(c))
{
c = tolower(c);
}
putchar(c);
i++;
}
return 0;
}
memcpy
在之前的分享中我与大家分享了strcpy
库函数,实现了字符串的拷贝,那么如果我想拷贝的是一个整形数组,是否有一个库函数可以实现我的需求呢?我们今天就来介绍一下memcpy
库函数,并跟大家一起模拟实现一下。
其实strcpy
与memcpy
的实现思路大致相同,只不过memcpy
的参数类型变成了void*
这样就可以实现不同类型数据的拷贝,并且memcpy
的参数中增加了一个size_t num
,num的含义是:从source开始复制num个字节的数据到destination中。好了了解了以上这些后,我们在vs中使用一下这个库函数。
#include<stdio.h>
#include<string.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr, 20);
return 0;
}
我们通过调试可以看到,通过调用mempy
库函数我们将arr
数组中的5个元素拷贝到了arr2
数组当中,为什么是五个元素呢?因为数组为整形数组一个元素占4个字节,我们调用memcpy
时传递给num
的值为20,所以需要拷贝20个字节的数据,所以拷贝了5个元素。
模拟实现
经过了上面的学习,我们可以尝试这自己模拟实现一下memcpy
这个库函数。
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr, 20);
return 0;
}
因为memcpy
函数的参数是void*
类型,我们在使用参数时,需要将参数强制类型转换成char*
类型,这样我们在拷贝数据时就可以一个字节一个字节的拷贝,当num
为0时循环结束,我们就拷贝了num
个字节的数据。
memmove
memmove
库函数与memcpy
库函数的主要区别就是,memmove
库函数需要实现重叠内存的拷贝,而memcpy
只需要实现不重叠内存的拷贝就可以了。
我们可以见memmove
库函数的函数声明与memcpy
库函数的声明其实是一样的,那么什么是重叠内存的拷贝呢?我们在代码中给大家举例说明。
模拟实现
如上图这个代码,我们想将红色区域的内容拷贝到绿色区域,最后的结果将数组变成1,2,1,2,3,4,5,8,9,10
类似于这样的拷贝就是重叠内存的拷贝,如果继续使用memcpy
我们会发现数组并没有变成我们想要的数组,是因为当我们在拷贝时,将数组的第三个元素变成了1
,当再次使用时元素3
已经被替换成了1
,所以没有达到我们期望的效果。那么如何解决这样的问题呢?当我们从前向后拷贝数组元素时,前面的数组元素会在拷贝前被改变,那么我们如果从后向前拷贝这样的问题是不是就是解决了呢。所以经过分析,当dest > src
时我们从后向前拷贝,其他情况我们从前向后拷贝。 我们将模拟实现的代码进行优化
void* my_memmove(void* dest, void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)//从前向后拷贝
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else//从后向前拷贝
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
return 0;
}
如图这样我们就模拟实现了库函数memmove
。