字符串函数和内存操作函数
前言:
文章中所提到的函数的头文件和每个函数的更详细介绍大家可以点击下面的链接去搜索查看。函数功能查询
一.求字符串长度
strlen
size_t strlen ( const char * str );
strlen是求字符串长度的函数。
注意:
1.字符串以’\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )
2.参数指向的字符串必须要以 ‘\0’ 结束。(否则函数就会一直找,直到找到’\0’为止,就可能出现随机数)
3.注意函数的返回值为size_t(unsigned int),是无符号的( 易错 )
接下来我们来看一串代码:
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf(">");
}
else
{
printf("<");
}
return 0;
}
大家觉得答案是什么呢?
那么为什么是大于?
在前面的注意的第三点中说到strlen的返回值是size_t,因此strlen(str2) - strlen(str1) == -3,但是此时的返回值是size_t,因此-3就被当成一个很大的正整数了。
二.长度不受限制的字符串函数
1.strcpy
char * strcpy ( char * destination, const char * source);
strcpy是一个字符串拷贝函数,将源字符串的数据拷贝到目标空间。
注意:
1.strcpy拷贝到’\0’结束,因此源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。
2.strcat
char * strcat ( char * destination, const char * source);
strcat是一个字符串追加函数,将源字符串追加到目标空间中。
注意:
1.追加是从目标字符串的’\0’处(第一个)追加,源字符串必须以 ‘\0’ 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。
4.会将源字符串中的’\0’追加到目标空间中
5.自己给自己追加时会将结束标志’\0’覆盖掉,导致无法结束,因此自己给自己追加时不使用strcat
3.strcmp
strcmp是字符串比较函数,比较对应位置上的字符的ASCII码值。
int strcmp ( const char * str1, const char * str2);
标准规定:
1.当str1 < str2 时,返回< 0 的数字
2.当str1 = str2 时,返回 0
3.当str1 > str2 时,返回> 0 的数字
为什么说它们是长度不受限制的字符串函数呢?
因为:
三.长度受限制的字符串函数
1.strncpy
char * strncpy ( char * destination, const char * source, size_t num);
将源字符串中的num个字符拷贝到目标空间。
1.当num < 源字符串的长度
2.当num = 源字符串的长度(包括\0)
3.当num > 源字符串的长度(包括\0)
小结:
1.如果源字符串的长度大于num,拷贝num个字符,但是不会拷贝\0。
2.如果源字符串的长度等于(包含\0)num,和strcpy相同。
3.如果源字符串的长度(包含\0)小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个。
2.strncat
char * strncat ( char * destination, const char * source, size_t num);
将源字符串中的num个字符追加到目标空间。
1.当num < 源字符串的长度
2.当num = 源字符串的长度(包括\0)
2.当num > 源字符串的长度(包括\0)
小结:
1.当num < 源字符串的长度时,因为追加之后依旧是一个字符串,所以会在末尾追加上\0。
2.当num = 源字符串的长度(包括\0),与strcat相同
3.当num > 源字符串的长度(包括\0),追加完源字符串(包括\0)后,不在追加。
3.strncmp
int strncmp ( const char * str1, const char * str2, size_t num);
strncmp和strncmp基本相同,只不过是增加了比较的字符个数。
四.字符串查找函数
1.strstr
const char * strstr ( const char * str1, const char * str2);
strstr函数的功能是,在str1中找str2。
找到子字符串(str2),返回子字符串第一次出现的地址。没有找到,返回空指针。
2.strtok
char * strtok ( char * str, const char * sep);
strtok是字符串切割函数,通过我们自定义的分隔符来切割字符串。
str是待切割的字符串,sep是我们自己定义的切割字符串的分隔符
注意:
1.sep参数是个字符串,定义了用作分隔符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回 NULL 指针。
接下来,博主结合代码为大家简单阐述strtok的功能:
优化:
int main()
{
char str[] = "123@456.789";
char sep[] = "@.";
char buf[20] = { 0 };
strcpy(buf, str);
char* ret = NULL;
for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
五.错误信息报告函数
1.strerror
char * strerror ( int errnum);
strerror的功能是:将错误码转换成错误信息,返回错误信息的起始地址。
接下来我们在VS的环境下来看一看每个错误码对应的错误信息:
我们可以看到,VS中每一个错误码对应一个错误信息,但是当我们使用函数时我们是不知道错误码的,因此这里在补充一个知识点。
int main()
{
//c语言的库函数,在运行的时候,如果发生错误
//就会把错误码存放在一个变量中errno(全局变量)
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("打开文件失败\n");
printf("%s\n", strerror(errno));
return 1;
}
fclose(pf);
pf = NULL;
return 0;
return 0;
}
2.perror
void perror ( const char * str);
功能:会直接打印错误信息,不过在打印错误信息之前,会先打印自定义信息
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("打开文件失败\n");
//printf("%s\n", strerror(errno));
perror("fope"); //"自定义信息"
return 1;
}
fclose(pf);
pf = NULL;
return 0;
return 0;
}
strerror和perror的区别:
strerror 是将错误码转换成错误信息,返回错误信息的起始地址(不打印) 要打印需要printf()
perror 是直接将错误信息打印在屏幕上
六.内存操作函数
1.memcpy
void * memcpy ( void * destination, const void * source, size_t num);
memcpy是内存拷贝函数,将source的num个字节的数据拷贝到destination中。
区别:
strcpy:是在针对字符串的拷贝函数,只能拷贝字符串
memcpy:可以拷贝任意类型
注意:
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\0’ 的时候并不会停下来, 只有将num个字节的数据拷贝完成后才会停下来。
3.如果source和destination有任何的重叠(内存重叠),复制的结果都是未定义的
何为内存重叠?
在最初的memcpy函数并不能实现内存重叠的拷贝也就是会出现1212121这样的结果(后期有一些编译器经过优化的memcpy能够实现内存重叠的拷贝)为此出现了memmove这样可以实现内存重叠的拷贝函数。
2.memmove
void * memmove ( void * destination, const void * source, size_t num);
memmove和memcpy基本相同,不过memmove可以实现内存重叠的拷贝(在有一些编译器下memcpy并不能够实现对内存重叠的拷贝)。
小结:
在使用内存拷贝函数时,我们可以使用memmove来代替memcopy
若要使用memcpy则需要考虑是否存在内存重叠,和编译器能否实现memcpy对内存重叠的拷贝。
3.memset
void * memset ( void * ptr, int value, size_t num);
memset是以字节为单位的内存设置函数,从ptr开始的num个字节设置成value
4.memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num);
memcmp是内存比较函数,比较ptr1和ptr2对应位置上的num个字节。
区别:
strcmp:只能比较字符串。
memcmp:可以比较任意类型。
ptr1和ptr2对应位置字节的大小
1.当str1 < str2 时,返回< 0 的数字
2.当str1 = str2 时,返回 0
3.当str1 > str2 时,返回> 0 的数字
总结:在对上述函数有一定了解之后,在下一篇博客中,博主将为大家带来上述函数的模拟实现,可以让大家更好的理解每一种函数是如何实现的。