字符串+内存函数的介绍
在学习这篇文档之前,我相信大家没有了解过strcat strstr strtok strerror
等这样的函数吧,而在我们日常学习的过程中,这些函数或多或少都会给我们起到很大帮助,那么我们接下来一起来学习一下各种字符串与内存相关的函数吧!
重点内容
重点介绍处理字符和字符串的库函数的使用和注意事项
- 求字符串长度
- strlen
- 长度不受限制的字符串函数
- strcpy
- strcat
- strcmp
- 长度受限制的字符串函数
- strncpy
- strncat
- strncmp
- 字符串查找
- strstr
- strtok
- 错误信息报告
- strerror
- 字符操作
- 内存操作函数
- memcpy
- memmove
- memset
- memcmp
1.strlen:求字符串长度
-
参数:
size_t strlen(const char* str);
- sizeof 操作符 计算大小的
-
字符串以
'\0'
作为结束标志,strlen函数返回的是在字符串中'\0'
前面出现的字符个数(不包
含'\0'
)。 -
参数指向的字符串必须要以
'\0'
结束。 -
注意函数的返回值为
size_t
(unsigned int),是无符号的( 易错 )#include<stdio.h> #include<string.h> int main() { if ((int)strlen("abc") - (int)strlen("abcdef") > 0) printf(">"); else printf("<=");//right if (strlen("abc") - strlen("abcdef") > 0) printf(">");//前-后=-3,strlen函数返回类型是size_t,-3会变成一个超级大的数>0 else printf("<="); return 0; }
-
使用
#include<stdio.h> #include<string.h> int main() { int len = strlen("abcdef"); printf("%d\n", len); printf("%d\n", strlen("abcdef"));//链式访问 return 0; }
-
模拟实现
#include<stdio.h> #include<string.h> #include <assert.h> int my_strlen(const char* str)//字符串不想被修改:加const { assert(str);//断言:判断指针是否有效 int count = 0; while (*str)//*str!='\0' { count++; str++; } return count; } int main() { int len = my_strlen("abcdef"); printf("%d\n", len);//6 return 0; }
2.strcpy:字符串的拷贝
-
参数:
char* strcpy(char * destination, const char * source );
-
将源字符串指向的字符串复制到目标指向的数组中,包括\0字符(并在该点停止)。
-
源字符串必须以 ‘\0’ 结束。
-
会将源字符串中的 ‘\0’ 拷贝到目标空间。
-
目标空间必须足够大,以确保能存放源字符串。
-
目标空间必须可变。
-
使用
#include<stdio.h> #include<string.h> int main() { char arr1[] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0' }; char arr2[20] = "xxxxxxxxxxxx"; printf("%s\n", strcpy(arr2, arr1));//返回arr2的起始地址 return 0; }
-
模拟实现
#include<stdio.h> #include<string.h> //方法一 char* my_strcpy(char* dest, const char* src) { char* ret = dest; assert(dest && src); //当*src为\0时,赋值过去\0,整个表达式为0,退出 while (*src) { *dest = *src; dest++; src++; } *dest = *src; //⽅法⼆ return ret;//返回⽬标字符串,模拟实现就是这么做的,⽅便进⾏链式访问 } //方法二 char* my_strcpy(char* dest, const char* src) { char* ret = dest; assert(dest && src); while (*dest++ = *src++) { ; } return ret;//返回⽬标字符串,模拟实现就是这么做的,⽅便进⾏链式访问 } int main() { //char arr1[] = "abcdef"; char arr1[] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0' };//没有\0拷贝失败 char arr2[20] = "xxxxxxxxxxxx"; //const char* p = "xxxxxxxxxx";//⽬标空间必须可变。 //my_strlen(p, arr1);//err 不能是const->⽬标空间必须可变 my_strcpy(arr2, arr1);//\0也应该拷⻉过去 printf("%s\n", arr2);//abcdef 后⾯的*不打印->因为把\0拷⻉过去了,遇到\0就不打印后⾯的东⻄了 return 0; }
注意事项:尽量不能返回局部变量的地址,返回局部变量可以
#include<stdio.h> //尽量不能返回局部变量的地址,返回局部变量可以 int Add(int x, int y) { int z = x + y; return z; } //空间销毁并不是地址被销毁 int* test() { int a = 10;//0x0012ff40 return &a;//不能返回局部变量的地址 } int main() { // p = 0x0012ff40 int* p = test(); *p = 20;//⾮法访问内存 return 0; }
3.strcat:字符串的连接
-
参数:
char * strcat ( char * destination, const char * source );
-
将源字符串的副本追加到目标字符串,目标字符串中的\0会被源字符串的第一个字符覆盖,并且在新拼接的两个字符串末尾会有\0
-
源字符串必须以 ‘\0’ 结束。
-
目标空间必须有足够的大,能容纳下源字符串的内容。
-
目标空间必须可修改。
-
字符串自己给自己追加,不会输出任何结果
#include<stdio.h> #include<string.h> int main() { char arr1[30] = "hello"; strcat(arr1, arr1); printf("%s\n", arr1); return 0; }
-
使用
#include<stdio.h> #include<string.h> int main() { char arr1[30] = "hello";//⽬标空间必须⾜够⼤ char arr2[] = "world"; //char arr2[] = { 'h','e','l','l','o' };//err //char arr2[] = { 'h','e','l','l','o','\0' };//原字符串必须以\0结束 strcat(arr1, arr2); printf("%s\n", arr1); return 0; }
-
模拟实现
#include<stdio.h> #include<string.h> char* my_strcat(char* dest, const char* src) { char* ret = dest; assert(dest && src);//判断空指针,不能判断是否是野指针 //1. ⽬标空间中的\0 while (*dest)//*dest!=‘\0’ { dest++; } //2. 追加内容到⽬标空间-strcpy while (*dest++ = *src++) { ; } return ret;//规定返回char*,⽬标空间起始地址 } int main() { char arr1[30] = "hello"; char arr2[] = "world";// {'w', 'o', 'r', 'l', 'd', '\0'}; printf("%s\n", my_strcat(arr1, arr2)); return 0; } 注意:两种错误方法 //到\0的位置,后置++就把\0跳过了 while(*dest++) { ; } //当arr1为空字符串(只有\0)的时候,上来就先把\0跳过去了 while(*++dest) { ; }
4.strcmp:字符串的比较
-
参数:
int strcmp ( const char * str1, const char * str2 );
-
这个函数刚开始会比较每个字符串的第一个字符的ASCII码值,之后继续比较,直到字符不同,或直到\0返回空指针。
-
标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
-
模拟实现
#include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2)//\0==\0也会进⼊循环 { if (*str1 == '\0') return 0; str1++; str2++; } return *str1 - *str2; /*if (*str1 > *str2)//>0 return 1; else//<0 return -1;*/ } int main() { //strcmp - 字符串⽐较 //⽐较是对应位置上的字符⼤⼩-⽐的是ASCII码值 char arr1[] = "abc"; char arr2[] = "abc"; int ret = my_strcmp(arr1, arr2); if (ret < 0)//ret==-1 err { printf("arr1<arr2"); } else if (ret > 0)//ret==1 err { printf("arr1>arr2"); } else { printf("arr1==arr2"); } return 0; }
-
判断字符串相等不能用==
if("abc"=="abq") //字符串的首地址一定不同->错误
5.strncpy:指定个数的字符串拷贝
-
参数:
char * strncat ( char * destination, const char * source, size_t num );
-
拷贝num个字符从源字符串到目标空间。
-
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个。
-
使用:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "xxxxxxxxxxxxxxxxx"; char arr2[] = "hello world"; strncpy(arr1, arr2, 5);//helloxxxxxxxxxxxx printf("%s\n", arr1); }
-
模拟实现
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strncpy(char* dest, const char* src, size_t n) { assert(dest&&src); char* ret = dest; while (n--) { *dest++ = *src++; } return ret; } int main() { char arr1[] = "xxxxxxxxxxxxxxxxx"; char arr2[] = "hello"; my_strncpy(arr1, arr2, 5); printf("%s\n", arr1); }
6.strncat:指定个数的字符串连接
-
参数:
char * strncat ( char * destination, const char * source, size_t num );
-
使用:
#include<stdio.h> #include<string.h> int main() { char arr1[20] = "hello"; char arr2[] = "world"; strncat(arr1, arr2, 3); printf("%s\n", arr1);//hellowor return 0; }
-
模拟实现:
#include<stdio.h> #include<string.h> #include<assert.h> void my_strncat(char* arr1, const char* arr2, int n) { assert(arr1 && arr2); char* p = arr1; char* q = arr2; while (*p) { p++; } while (n--) { *p = *q; p++; q++; } *p = '\0'; } int main() { char arr1[30] = "hello"; const char arr2[] = "world"; int num = 0; scanf("%d", &num); my_strncat(arr1, arr2, num); printf("%s\n", arr1); return 0; }
7.strncmp:指定个数的字符串比较
-
参数:
int strncmp ( const char * str1, const char * str2, size_t num );
-
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
-
返回值 说明 <0 arr1<arr2 >0 arr1>arr2 =0 arr1==arr2 -
使用:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "abcwef"; char arr2[] = "abcqqqqqq"; int ret = strncmp(arr1, arr2, 4); if (ret < 0) { printf("arr1<arr2"); } else if (ret > 0) { printf("arr1>arr2");//w>q } else { printf("arr1==arr2"); } return 0; }
-
模拟实现:
#include<stdio.h> #include<string.h> #include<assert.h> int my_strncmp(const char* str1, const char* str2, int n) { assert(str1 && str2); while (n--) { if (*str1 == *str2) { str1++; str2++; } else if (*str1 - *str2 < 0) { return -1; } else { return 1; } } return 0; } int main() { char arr1[] = "abcwef"; char arr2[] = "abcqqqqqq"; int ret = my_strncmp(arr1, arr2, 4); if (ret < 0) { printf("arr1<arr2"); } else if (ret > 0) { printf("arr1>arr2");//w>q } else { printf("arr1==arr2"); } return 0; }
8.strstr:字符串查找函数
-
参数:
char * strstr ( const char *str1, const char * str2);
-
返回一个指向str1中首次出现str2的指针,如果str2不是str1的一部分,则返回一个空指针。
-
使用:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbcq"; char* ret = strstr(arr1, arr2); if (NULL == ret) printf("没找到\n"); else printf("%s\n", ret); return 0; }
-
模拟实现:
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strstr(const char* str, const char* substr)//找substr { const char* s1 = str;//原来的字符串的起始地址 const char* s2 = substr;//目标字符串起始字符串地址 const char* cur = str; assert(str && substr); if (*substr == '\0')//判断子字符串是否是空字符串-没法查找 { return (char*)str;//规定:返回str } while (*cur)//*cur!=‘\0’ 后⾯还有元素可以寻找 { s1 = cur; s2 = substr; while (*s1 && *s2 && *s1 == *s2) { s1++; s2++; } if (*s2 == '\0')//把⼦串找到了,返回它的地址 return (char*)cur; cur++;//这个字符不相等,向后移 } return NULL;//未找到 } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbcq"; char* ret = my_strstr(arr1, arr2); if (NULL == ret) printf("没找到\n"); else printf("%s\n", ret); return 0; }
9.strtok:把一个字符串分割成几个字符串
-
参数:
char * strtok ( char * str, const char * sep );
-
sep参数是个字符串,定义了用作分隔符的字符集合
-
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。 -
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。) -
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。 -
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。 -
如果字符串中不存在更多的标记,则返回 NULL 指针。
-
使用:
#include <stdio.h> #include <string.h> int main() { const char* p = "@.#";//要先加分隔符 char arr[] = "shuaige@zui.shuai#hehe"; char buf[50] = { 0 }; strcpy(buf, arr); char* str = NULL;//先初始化 for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p)) { printf("%s\n", str); } return 0; } //注意事项: //1.strtok函数找第⼀个标记的时候,函数的第⼀个参数不是NULL //2.strtok函数找⾮第⼀个标记的时候,函数的第⼀个参数是NULL
10.strerror:返回错误码,所对应的错误信息
-
参数:
char * strerror ( int errnum );
-
在C语言中,规定了一些信息—错误码(错误信息)
- 0 - “No Error”
- 1-
- 2-
- …
//strerror - 可以把错误码翻译成错误信息 int main() { int i = 0; for (i = 0; i < 10; i++) { printf("%s\n", strerror(i)); } return 0; }
-
使用:
//C语⾔可以操作⽂件 //打开⽂件 - fopen // //当库函数使⽤的时候,发⽣错误会把errno这个全局的错误变量设置为本次执⾏库函数产⽣的错误码 //errno是C语⾔提供的⼀个全局变量,可以直接使⽤,放在errno.h⽂件中的 #include <stdio.h> #include <errno.h> int main() { //打开⽂件 FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { //出错误的原因是什么 printf("%s\n", strerror(errno)); return 0;//打开失败直接结束 } //读⽂件 //... //关闭⽂件 fclose(pf); pf = NULL; return 0; }
11.常见字符分类函数
函数 | 如果他的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace 空 | 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母az或AZ |
isalnum | 字母或者数字,az,AZ,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
举例:
#include<stdio.h>
#include<ctype.h>
//判断是否是空⽩字符
int main()
{
//printf("%d\n", isspace(' '));//8-打印看不⻅的字符时返回⼀个⾮0值
//printf("%d\n", isspace(' '));//0-是随机的
char ch = 'w';
if (isspace(ch))
{
//空⽩字符
}
else
{
//⾮空⽩字符
}
return 0;
}
//判断是不是数字
int main()
{
char ch = '0';
//if (ch >= '0' && ch <= '9')//效率低
//{
//}
if (isdigit(ch))
{
}
//转换函数:⼤写字⺟⼩写字⺟互换
char ch = 0;
ch = getchar();
if (islower(ch))//判断⼩写
{
ch = toupper(ch);//⼩写->⼤写
}
else//判断⼤写
{
ch = tolower(ch);//⼤写->⼩写
}
printf("%c\n", ch);
return 0;
}
12.memcpy:指定个数的字符串拷贝
-
参数:
void * memcpy ( void * destination, const void * source, size_t num );
-
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
-
这个函数在遇到 ‘\0’ 的时候并不会停下来。
-
如果source和destination有任何的重叠,复制的结果都是未定义的。
-
使用:
#include<stdio.h> #include<ctype.h> int main() { //char arr1[] = "abcdef"; //char arr2[20] = { 0 }; //strcpy(arr2, arr1);//只能拷⻉字符串的 int arr3[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr4[5] = { 0 }; memcpy(arr4, arr3, 20); int i = 0; for (i = 0; i < 5; i++) { printf("%d\n", arr4[i]);//1 2 3 4 5 } return 0; }
-
模拟实现
#include<stdio.h> #include<ctype.h> #include<assert.h> void* my_memcpy(void* dest, const void* src, size_t num) { void* ret = dest; assert(dest && src); while (num--) { *(char*)dest = *(char*)src;//(char*)dest 一个一个字节操作起来会更加细致 dest = (char*)dest + 1;//dest++;err 强制类型转换都是临时的,需要每次都强转 src = (char*)src + 1;//src++;err } return ret;//要求规定:返回一个void*类型的指针 } int main() { int arr3[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr4[5] = { 0 }; my_memcpy(arr4, arr3+5, 5*sizeof(arr3[0])); int i = 0; for (i = 0; i < 5; i++) { printf("%d\n", arr4[i]);//6 7 8 9 10 } return 0; }
反例:当原字符串和目标字符串重叠的时候->memmove可以解决
13.memmove:包含了memcpy
-
参数:
void * memmove ( void * destination, const void * source, size_t num );
-
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
-
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
-
举例:
#include<stdio.h> #include<ctype.h> #include<assert.h> //反例:当原字符串和⽬标字符串重叠的时候->memmove可以解决 //memmove void* my_memcpy(void* dest, const void* src, size_t num) { void* ret = dest; assert(dest && src); while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return ret; } //memmove实现 void* my_memmove(void* dest, const void* src, size_t num) { void* ret = dest; assert(dest && src);//保证两个指针的有效性 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; } void test1() { int arr3[] = { 1,2,3,4,5,6,7,8,9,10 }; //my_memcpy(arr3+2, arr3, 5 * sizeof(arr3[0]));//1 2 1 2 1 2 1 8 9 10 //my_memmove(arr3, arr3 + 2, 20);//3 4 5 6 7 6 7 8 9 10 memcpy(arr3+2, arr3, 20);//1 2 1 2 3 4 5 8 9 10 //库⾥⾯的memcpy⽀持,但是C语⾔只要求: //memcpy能拷⻉不重叠的内存空间就可以了 //memmove去处理那些重叠内存拷⻉ //但是发现VS上memcpy也能实现重叠拷⻉(超常发挥了) int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr3[i]); } } int main() { test1(); return 0; }
14.memcmp:内存比较函数
-
参数:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
-
比较从ptr1和ptr2指针开始的num个字节
-
返回值:
15.memset函数
-
使用
int main() { //char arr[20] = { 0 }; //memset(arr, 'x', 10); int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; memset(arr, 0, 10); //00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 ... return 0; }
memcpy(arr3+2, arr3, 5 * sizeof(arr3[0]));//1 2 1 2 1 2 1 8 9 10 //my_memmove(arr3, arr3 + 2, 20);//3 4 5 6 7 6 7 8 9 10 memcpy(arr3+2, arr3, 20);//1 2 1 2 3 4 5 8 9 10 //库⾥⾯的memcpy⽀持,但是C语⾔只要求: //memcpy能拷⻉不重叠的内存空间就可以了 //memmove去处理那些重叠内存拷⻉ //但是发现VS上memcpy也能实现重叠拷⻉(超常发挥了) int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr3[i]); } } int main() { test1(); return 0; }
到这里,C语言中常见的字符串就已经讲解完毕了,希望你能理解的透彻!
喜欢的话一件三连熬~