一、strtok(字符串分割)
1.1函数原型
1.2函数解释
strTokeen是要被分割的字符串,该字符串中存在一个或多个分割符
strDelimit是分割srrToken的标记,也就是分割符,可以是一个分割符,也可以是多个分割符的集合
strtok函数的第一个函数参数如果不为NULL时,它会寻找被分割字符串中的分割符,找到任意的分割符时会将该分割符改为\0,并返回这一子串首字符的地址,并且记住该分割符的位置
当第一个函数参数为NULL时,往往是在我们已经调用过一次该函数,并且第一个函数参数不为空,不为空那么该函数就会记忆分割符的位置,当我们第二次调用并且第一个函数参数为NULL时,函数就会从字符串被记忆的分割符的位置开始再次寻找下一个分割符,找到任意的分割符时会将该分割符改为\0,并返回这一子串首字符的地址,并且记住该分割符的位置,当找不到分割符时,就会返回NULL
对于字符串分割函数我们可以这样理解,在被分割字符串中的分割符将被分割字符串分成了若干个子字符串,函数每一次都会在被分割字符串中寻找我们第二个字符串里的分割符集合,只要找到标记(也就是分割符),就会停止寻找,并且将标记改为\0,并返回第一个子串首字符的地址,同时会记住该标记的位置
当使用该函数的时候,第一次调用的时候,第一个参数是要被分割的字符串,第二个参数是我们要分割的前提,也就是标记,第二次调用的时候,第一个参数就为NULL,第二个参数不变,函数会记住我们要分割的字符串,并且第一个标记的位置所在,因此我们可以用一个巧妙的办法来实现函数的多次调用
1.3函数使用
为了方便大家实验该函数,请看如下代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>//strtok函数所需头文件
void test()
{
char arr[] = "i-like-beijing";
//被分割字符串,存在分割符-
char arr1[] = "i-really@like?beijing";
//被分割字符串,存在分割符-@?
char* p = "-";//要分割arr使用到的分割符集合
char* p1 = "-@?";//要分割arr使用到的分割符集合
char* ptr = NULL;
char* ptr1 = NULL;
for (ptr = strtok(arr, p); ptr != NULL; ptr = strtok(NULL, p))
//初始条件即为strtok函数的第一次调用,不为NULL则开始第二次调用,以此类推
//找不到标记则返回NULL,循环结束
{
printf("%s ", ptr);//打印每一个被分割符分割的子串
}
printf("\n");
for (ptr1 = strtok(arr1, p1); ptr1 != NULL; ptr1 = strtok(NULL, p1))
{
printf("%s ", ptr1);
}
}
int main()
{
test();
}
二、strerror
2.1函数原型
2.2函数解释
errnum是错误码,它是一个整型的数字,对于该函数而言,不同的数字对应着不同的错误信息,strerror函数就是翻译错误码所对应的错误信息,并返回错误信息的首地址,
而当C语言中的库函数调用失败的时候,也会产生一个错误信息,并且会将这个错误信息储存在名为errno的整型变量中
因此,strerror函数在使用的时候,往往会和errno配套使用,以此来检验某个库函数是否调用失败,需要一提的是,要想使用errno,必须包含头文件#include<errno.h>
2.3函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<errno.h>
void test1()
{
FILE* f = fopen("long.txt", "r");
//fopen是一个库函数,该语句的意思是打开一个叫long.tet的文件,r表示以
//只读的方式打开,打开失败会返回NULL,而这个文件是随便起的,不会存在,
//因此该库函数必然调用失败
if (f == NULL)//失败则打印错误信息
{
printf("%s", strerror(errno));
//结合errno打印错误信息
return 1;
}
fclose(f);
f = NULL;
}
int main()
{
test1();
}
三、strncpy(字符串拷贝)
3.1函数原型
3.2函数解释
从源头字符串(strDest)的第一个字符开始,向目标字符串的第一个字符开始拷贝count个字符,不会追加0,不足count个则补0
相比于strcpy,strncpy的函数参数多了一个count,用来控制拷贝字符的个数,变为了长度受限制的字符串函数,这样相对来说,在使用函数的时候会更加安全,
strncpy函数的功能也是字符串拷贝,只不过它会严格按照count的大小来进行拷贝,当count的大小小于源头字符串的长度时,在拷贝完成后,并不会拷贝自动添加\0,而大于的时候,会以0作为补充,一直到拷贝个数等于count
3.3函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void test2()
{
char arr1[] = "abcdef";
char arr2[] = "xxxxxxxxxx";
strncpy(arr2, arr1, 8);
printf("%s\n", arr2);
}
int main()
{
test2();
}
经过调试,我们可以看到,要拷贝字符的个数小于源头字符串时,确实不会再拷贝完成后添加\0,当要拷贝字符的个数大于源头字符串时,确实会添加0知道拷贝个数达到要求
四、strncat(字符串连接)
4.1函数原型
4.2函数解释
找到目标字符串(strDest)中的\0,将从源头字符串第一个字符开始的count个字符从目标字符串的\0开始拷贝(或者叫追加),拷贝完后追加\0
相比strcat,strncat,strncat的函数参数多了一个count,用来控制拷贝字符的个数,变为了长度受限制的字符串函数,这样相对来说,在使用函数的时候会更加安全,同时不管连接几个字符,连接完后都会追加\0
从功能来看,都是实现字符串的连接
4.3函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void test3()
{
char arr1[] = "abc\0xxxxxxxxxxxx";
char arr2[] = "defghijk";
strncat(arr1, arr2, 10);
printf("%s", arr1);
}
int main()
{
test3();
}
我们可以看到,当在arr1后连接arr2的10个字符时,在arr1中确实是从\0开始拷贝arr2中的字符的,并且arr2加上\0只有9个字符,虽然要连接10个字符,但是并不会补0来凑足个数
补充:strncat可以自己连接自己,而strcat不行
五、strncmp(字符串比较)
5.1函数原型
5.2函数解释
依次比较两个字符串上前count个对应位置字符的ASCIL值,
也就是string1的第一个字符对应的ASCIL值与string2的第一个字符的ASCIL比较,大于则返回正数,小于则返回负数,等于则继续比较,知道比较完第count个字符,如果相等,则返回0
5.3函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void test4()
{
char string1[] = "abcdef";
char string2[] = "abc";
int ret=strncmp(string1, string2, 3);
printf("%d", ret);
}
int main()
{
test4();
}
如图所示,比较三个字符时,返回0,比较四个字符时,因为d的ASCIL值大于0的,因此返回正数1
六、内存函数(memcpy)
6.1函数原型
6.2函数解释
内存拷贝函数,只要是存在于内存中的数据,无论什么类型都可以拷贝
voiddest和const voidsrc,分别表示目标数据和源头数据,void*可以保证接收任意类型的数据,count表示字节数
该函数的作用就是,将src中20个字节的数据拷贝到dest当中去
6.3函数使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
void test()
{
float arr[] = { 1.0,2.0,3.0,4.0,5.0 };
float arr2[10] = { 0 };
memcpy(arr2, arr, 12);
//将arr中前12个字节的数据拷贝到arr2当中去
}
int main()
{
test();
}
需要注意的是,memcpy的两个参数不可以是同一块内存的不同位置(因为某些编译器可以做到和memmove相同的功能,有些不行),即不可以是一个数组的两个不同位置,否则会有如下覆盖问题
6.4my_memcpy
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t count)
{
void* ret = dest;
//记忆目标数据初始地址
assert(dest && src);//断言非空
while (count--)
{
*(char*)dest = *(char*)src;
//按字节拷贝
dest = (char*)dest + 1;//指向下一字节处
src = (char*)src + 1;//指向下一字节处
}
return ret;
}
void test()
{
float arr[] = { 1.0,2.0,3.0,4.0,5.0 };
float arr2[10] = { 0 };
my_memcpy(arr2, arr, 8);
for (int i = 0; i < 5; i++)
{
printf("%f ", arr2[i]);
}
}
int main()
{
test();
}
七、内存函数(memmove)
7.1函数原型
7.2函数解释
函数参数完全和memcpy相同,如果说memcpy针对的是不同内存数据的拷贝,那么memmove针对的就是同一内存下数据的移动
7.3函数使用
移动同一内存不同位置的数据,并且避免了覆盖,
覆盖的意思是,将3.0,4.0,5.0移动到1.0,2.0,3.0位置时,如果先将5.0移动到3.0的位置,那么在移动3.0的时候,3.0已经被5.0覆盖,变成了5.0,因此无法有效移动
7.4my_memmove
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t count)
{
void* ret = dest;//记忆位置
assert(dest);//断言非空
if (dest < src)
//如果要移动的两块区域存在重叠,并且dest在前,那么从前向后移动
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
//如果要移动的两块区域存在重叠,并且dest在后,那么从后向前移动
//或者两块区域不存在重叠,那么顺序无影响,但是我们选择从后向前
{
dest = (char*)dest + count - 1;
src = (char*)src + count - 1;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
void test()
{
float arr[] = { 1.0,2.0,3.0,4.0,5.0 };
my_memmove(arr, arr+2, 12);
for (int i = 0; i < 5; i++)
{
printf("%f ", arr[i]);
}
}
int main()
{
test();
}