目录
字符串函数
strtok 截取分隔符
char * strtok ( char * str, const char * sep );
重点
-
sep参数是个字符串,定义了用作分隔符的字符集合;
-
str参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记;
-
strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
-
strtok函数的第一个参数不为NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
下述例子,第一次调用strtok()函数返回第一个y的地址。打印
yingyq0118
。 -
strtok函数的第一个参数为NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。对于同一个字符串,要多次分割,第2次及以后的第一个参数要传NULL!!
例如同一个字符串有两个分隔符时,下一次要取到
163
时,要写strtok(NULL, sep);
,下一次取得com
时,也是写strtok(NULL, sep);至此,用作分隔符的字符已不存在,无需再次调用,再次调用则返回NULL。 -
如果字符串中不存在更多的标记,则返回NULL 指针。
作用举例
邮箱由三个部分组成,@ .可以当作分隔符,用strtok切割成三个部分。
那么这里
//这个很弱智,只是为了详细说明函数如何使用,分开调用strtok
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "yingyq0118@163.com";
char buf[200] = { 0 };//用于存放拷贝的内容
strcpy(buf, arr);
const char* sep1 = "@.";
char sep2[2] = { '@','.' };
char* str1 = strtok(buf, sep2);
printf("%s\n", str1);
char* str2 = strtok(NULL, sep2);
printf("%s\n", str2);
char* str3 = strtok(NULL, sep2);
printf("%s\n", str3);
return 0;
}
//这个才是实际使用
int main()
{
char arr[] = "yingyq0118@163.com";
char buf[200] = { 0 };//用于存放拷贝的内容,strtok的第一个参数
strcpy(buf, arr);
const char* sep = "@.";//strtok的第二个参数
char* str = NULL;//保存strtok返回的地址
for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
strerror 把错误码转换成错误信息
char * strerror ( int errnum );
重点
返回错误码,所对应的错误信息。
如果不想打印错误信息,就用strerror
#include <stdio.h>
#include <string.h>
int main()
{
printf("%s\n", strerror(0));//No error
printf("%s\n", strerror(1));//Operation not permitted
printf("%s\n", strerror(2));//No such file or directory
printf("%s\n", strerror(3));//No such process
printf("%s\n", strerror(4));//Interrupted function call
return 0;
}
//0,1,2,3,4,是c语言库函数报错时返回的字符码。
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件 为了使用errno
//errno是c语言提供的全局的错误变量
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
//错误码会被记录到 错误码变量errno里面
printf("%d\n", errno);
printf("%s\n", strerror(errno));
return 1;
}
//处理文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
perror
void perror ( const char * str );
看实际的输出结果,可以猜想perror = printf + strerror
重点
str是用户自定义的输入,函数会自己去拿errno变量中错误码的信息。
如果你想打印错误信息,就用perror
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
//错误码会被记录到 错误码变量errno里面
perror("user");//yyq: No such file or directory
return 1;
}
//处理文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
//输出:user: No such file or directory
字符分类函数
函数 | 如果他的参数符合下列条件就返回真 | 头文件 |
---|---|---|
iscntrl | 任何控制字符 | |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v' | |
isdigit | 十进制数字 0~9 | ctype.h |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F | ctype.h |
islower | 小写字母a~z | ctype.h |
isupper | 大写字母A~Z | ctype.h |
isalpha | 字母a~z或A~Z | ctype.h |
isalnum | 字母或者数字,a~z,A~Z,0~9 | ctype.h |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) | |
isgraph | 任何图形字符 | |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换函数
函数定义 | 功能 | 头文件 |
---|---|---|
int toupper ( int c ); | 小写转大写 | ctype.h |
int tolower ( int c ); | 大写转小写 | ctype.h |
内存函数
memcpy
void * memcpy ( void * destination, const void * source, size_t num );
重点
-
区别:memcpy什么类型的数据都能拷贝(因为接受的是void*),但是strcpy strncpy只能拷贝char类型的变量;
-
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置;
-
这个函数在遇到'\0' 的时候并不会停下来;
-
警告:如果source和destination在内存空间有任何的重叠,复制的结果都是未定义的。
模拟实现
void* my_memcpy(void* destination, const void* source, size_t num)
{
assert(destination && source);
void* dest = destination;//保存首元素的地址
while (num--)//1个字节1个字节的拷贝
{
*(char*)destination = *(char*)source;//拷贝当前字符
destination = (char*)destination + 1;//向后走1个字节
source = (char*)source + 1;//向后走1个字节
}
return dest;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 5 * sizeof(int));//拷贝前5个元素
for (int i = 0; i < 5; i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
return 0;
}
arr1在内存中存储为
01000000 02000000 03000000 04000000 05000000.....
低地址--------------------------------------------------------高地址
一次拿1个字节
00000000 00000000 00000000 00000000 00000000.....
低地址--------------------------------------------------------高地址
得到
01000000 02000000 03000000 04000000 05000000.....
低地址--------------------------------------------------------高地址
注意:小Tips
提问:为什么VS里用memcpy也可以实现重叠内存的拷贝?
C语言规定memcpy只需要实现非重叠内存的拷贝,只不过VS里面的memcpy超额完成而已。
当要拷贝的source空间和指向的destination目的空间有内存重叠时:
当destination小于source,应该从前向后(从低地址先高地址处)拷贝;
当destination大于source,应该从后向前(从高地址向低地址处)拷贝。
内存重叠要看下面的memmove。
memmove
void * memmove ( void * destination, const void * source, size_t num );
重点
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的;
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
当要拷贝的source空间和指向的destination目的空间有内存重叠时(出现在同一个变量使用内存拷贝的情况):
当(char*)destination
小于(char*)source
,应该从前向后(从低地址先高地址处)拷贝,将source的开头元素拷贝到destination的开头元素位置处,对指针destination、source依次++;
当(char*)destination
大于(char*)source
,应该从后向前(从高地址向低地址处)拷贝,将source的末尾元素拷贝到destination的末尾元素处,对num--。
模拟实现
void* my_memmove(void* destination, const void* source, size_t num)
{
assert(destination && source);
void* dest = destination;//保存首元素的地址
if (destination < source)//数组地址随着数组下标的增加而增大
{
//从前向后拷贝
while (num--)
{
*(char*)destination = *(char*)source;//拷贝当前字符
destination = (char*)destination + 1;//向后走1个字节
source = (char*)source + 1;//向后走1个字节
}
}
else
{
//从后向前拷贝
while (num--)
{
*((char*)destination + num) = *((char*)source + num);//拷贝当前字符
}
}
return dest;
}
memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
重点
比较从ptr1和ptr2指针开始的num个字节
返回值 | 描述 |
---|---|
<0 | 第一个字节 ptr1所指向的元素比ptr2所指向的元素小(假设比较的是unsigned char类型的数据) |
0 | 第一个字节 两个指针所指向的元素大小相等 |
>0 | 第一个字节 ptr1所指向的元素比ptr2所指向的元素大(假设比较的是unsigned char类型的数据) |
memset
void * memset ( void * ptr, int value, size_t num );
重点
-
ptr:要修改的空间;
-
value:要修改成的值,只能占一个字节;
-
num:要修改的字节数。
-
memset时按字节来修改内存内容。在使用时要小心,容易越界访问。