文章时效性提示
本文发布于 507 天前,部分信息可能已经改变,请注意甄别。
求字符串长度
长度不受限制的字符串函数
长度受限制的字符串函数
字符串查找
错误信息报告
字符操作
内存操作函数
- memcpy
- memmove
- memset
- memcmp
C语言字符串通常放在常量字符串和字符数组中。
strlen
strlen函数是用来求字符串长度的
size_t strlen(const char *string)
返回类型是size_t就是unsigned int,即无符号整型,因为返回的长度不会是负数。根据这个特性,不能用两个strlen的返回值相减,这样得到的结果不是想要的。
1 2 3 4 5 6 7
| #include <stdio.h> #include <string.h> int main() { int len = strlen("abcdef"); printf("%d\n",len); return 0; }
|
字符串len有6个元素,打印的结果是6。
strlen的原理就是求出一个字符串中\0元素之前的元素个数。
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char arr[] = {'a','b','c','d','e'}; int len = strlen(arr); printf("%d\n",len); return 0; }
|
上面的打印结果是随机值,因为arr这个字符串数组中没有\0,strlen会一直增加,直到它找到了下一个\0。
模拟实现strlen:
1、计数器方法
2、递归方法
3、指针-指针的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <assert.h> int my_strlen(const char* str) { int count = 0; assert(str != NULL); while (*str != '\0') { count++; str++; } return count; }
int main() { int len = my_strlen("abcdef"); printf("%d\n",len); return 0; }
|
strcpy
char* strcpy(char * destination,const char * source);
字符串拷贝,从源头拷贝到目的地。
注意⚠️:
原字符串必须以\0结束。
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char arr1[] = "abcdefghijkl"; char arr2[] = "hello"; strcpy(arr1,arr2); printf("%s\n%s",arr1,arr2); return 0; }
|
打印的结果是hello和hello。
拷贝时把arr2中的hello和\0一并拷贝到了arr1中
模拟实现strcpy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <assert.h> char* my_strcpy(char* dest,const char* src) { assert(dest != NULL); assert(src != NULL); char* ret = dest; while (*dest++ = *src++) { ; } return ret; }
int main() { char arr1[] = "abcdefghijkl"; char arr2[] = "hello"; my_strcpy(arr1,arr2); printf("%s\n%s",arr1,arr2); return 0; }
|
strcat
字符串追加。
char * strcat (char * destination, const char * source);
注意⚠️:目的地一定足够大,能放下追加的字符串
源头一定要以\0结尾
使用strcat,不能自己给自己追加(即arr不能又追加arr)
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char arr1[30] = "hello"; char arr2[] = "world"; strcat(arr1,arr2); printf("%s\n",arr1); return 0; }
|
打印的结果是helloworld
模拟实现strcat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <assert.h> char* my_strcat(char* dest ,const char* src) { char* ret = dest; assert(dest != NULL); assert(src != NULL); while (*dest != '\0') { dest++; } while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[30] = "hello"; char arr2[] = "world"; my_strcat(arr1,arr2); printf("%s\n",arr1); return 0; }
|
strcmp
int strcmp (const char * str1,const char * str2);
字符串比较。对应字符比较,即先比较首字符,相等就往后比。
| 返回值 |
|---|
| str1 < str2 | <0 |
str1 = str2
| 0 |
str1 > str2
| >0 |
| 返回值根据不同编译器可能会不同,但符合返回值规律。 | |
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char *p1 = "abcdef"; char *p2 = "ghijk"; int ret = strcmp(p1,p2); printf("%d\n",ret); return 0; }
|
返回的结果是-6(或任何一个负数),原因是a的ASCII值比g小,返回小于0的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <stdio.h> #include <string.h> int main() { char *p1 = "kbcdef"; char *p2 = "ghijk"; if (strcmp(p1,p2) > 0) { printf("p1 > p2\n"); }else if(strcmp(p1,p2) < 0) { printf("p1 = p2\n"); }else { printf("p1 < p2\n"); } return 0; }
|
模拟实现strcmp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 != NULL); assert(str2 != NULL); while (*str1 == *str2) { if (*str1 == '\0') { return 0; } str1++; str2++; } if (*str1 > *str2) { return 1; }else { return -1; } }
int main() { char *p1 = "dbcdef"; char *p2 = "abcdef"; int ret = my_strcmp(p1, p2); printf("ret = %d\n",ret); return 0; }
|
strncpy
char *strncpy(char *strDest,const cahr *strSource,size_t count);
size_t count的单位是字节
拷贝num个字符从原字符串到目标空间。
如果原字符串的长度小于num,则拷贝完原字符串后,在后边追加\0,直到第num个。
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char arr1[5] = "abc"; char arr2[] = "hello"; strncpy(arr1,arr2, 4); printf("%s\n",arr1); return 0; }
|
打印的结果是hell
strncat
char *strncat(char *strDest,const char *strSource,size_t count);
追加num个字符从Source到Dest(拷贝到Dest后面)
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { char arr1[30] = "abcdef"; char arr2[] = "hello"; strncat(arr1,arr2, 4); printf("%s\n",arr1); return 0; }
|
打印的结果是abcdefhell
strncmp
int strncmp(const char *string 1,const char *string 2,size_t count);
字符串比较。
size_t count是比较的字符数
| 返回值 |
|---|
| string1 < string2 | <0 |
string1 = string2
| 0 |
string1 > string2
| >0 |
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { const char *p1 = "abcdef"; const char *p2 = "abcdaaa"; int ret = strncmp(p1,p2, 5); printf("%d\n",ret); return 0; }
|
打印的结果是4(不同编译器可能打印结果不同,但都大于0)
strstr
char *strstr(const char *string,const char *strCharSet)
查找子字符串。
查找子串是否存在,存在返回第一次出现的起始地址,不存在返回NULL
const char *string是要查找的字符串(在这个字符串里查找)
const char *strCharSet是要找的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <stdio.h> #include <string.h> int main() { const char *p1 = "abcdefghijk"; const char *p2 = "defa"; char* ret = strstr(p1, p2); if (ret == NULL) { printf("没找到\n"); } else { printf("子串存在,是:%s\n",ret); } return 0; }
|
查找子串是否存在,存在返回第一次出现的起始地址,不存在返回NULL
模拟实现strstr:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <stdio.h> #include <assert.h> char* my_strstr(char* p1,const char* p2) { assert(p1 != NULL); assert(p2 != NULL); char *s1 = p1; char *s2 = p2; char *current = (char*)p1; if (*p2 == '\0') { return (char*)p1; } while (*current) { s1 = current; s2 = (char*)p2; while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2)) { s1++; s2++; } if (*s2 == '\0') { return current; } current++; } return NULL; } int main() { const char *p1 = "abcdef"; const char *p2 = "def"; char* ret = my_strstr(p1, p2); if (ret == NULL) { printf("没找到\n"); } else { printf("子串存在,是:%s\n",ret); } return 0; }
|
strtok
char * strtok (char *str, const char * sep);
sep参数是个字符串,定义了作为分隔的字符集合
例如一个ip地址,192.168.123.234,它就是由点.分隔的
或者一个邮箱example@eee.com,它就是由@和.分隔的
str就是要被分隔的字符串,其中有或者没有 sep 中的字符都行。
strtok在str中找到了sep中规定的字符,就把这个字符改成\0,并返回指向这个标记的指针。
例如example@eee.com返回example,再次调用,在之前的前提下,寻找下个标记,找到了.点,并返回下个首字符,即eee
一般使用strtok函数,都要临时拷贝一份,切割拷贝的那份,避免原数据找不到了
strtok函数的第一个参数不是NULL,函数找到str中第一个标记,strtok函数保存它在字符串中的位置。
strtok函数的第一个参数是NULL,函数在同一个字符串中被保存的位置开始,查找下一个标记。
字符串中没有更多标记,返回NULL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h> #include <string.h> int main() { char arr[] = "example@eee.com"; char *p = "@."; char buf[1024] = {0}; strcpy(buf, arr); char* ret = strtok(buf, p); printf("%s\n",ret); ret = strtok(NULL, p); printf("%s\n",ret); ret = strtok(NULL, p); printf("%s\n",ret); return 0; }
|
打印的结果是:
上面代码的改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> #include <string.h> int main() { char arr[] = "example@eee.com"; char *p = "@."; char buf[1024] = {0}; strcpy(buf, arr); char *ret = NULL; for (ret = strtok(arr,p); ret != NULL; ret = strtok(NULL, p)) { printf("%s\n",ret); } }
|
上面函数的for循环初始化部分ret = strtok(arr,p)只会运行一次,后面都会运行ret = strtok(NULL, p)。
strerror
把错误码转换成所对应的错误信息
char * strerror(int errnum);
1 2 3 4 5 6 7 8
| #include <stdio.h> #include <string.h> int main() { char* str = strerror(0); printf("%s\n",str); return 0; }
|
打印的结果是:Undefined error: 0
1 2 3 4 5 6 7 8
| #include <stdio.h> #include <string.h> int main() { char* str = strerror(1); printf("%s\n",str); return 0; }
|
打印的结果是:Operation not permitted
errno:是全局错误码的变量,当C语言库函数在执行过程中出现错误,会把错误码给errno中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h> #include <string.h> #include <errno.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n",strerror(errno)); } else { printf("打开成功\n"); } }
|
打印的结果是:No such file or directory
字符分类函数
使用需要引用头文件#include <ctype.h>
| 函数 | 如果它的参数符合下列条件就返回真 |
|---|
| iscntrl | 任何控制字符 |
| isspace | 空白字符:空格’ ‘,换页’\f’,换行’\n’,回车’\r’,制表符’\t’或垂直制表符’\v’ |
| isdigit | 十进制数字 0-9 |
| isxdigit | 十六进制数字,包括所有十进制数字,小写字母a-f,大写字母A-F |
| islower | 小写字母a-z |
| isupper | 大写字母A-Z |
| isalpha | 字母a-z或A-Z |
| isalnum | 字母或着数字a-z,A-Z,0-9 |
| ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
| isgraph | 任何图形字符 |
| isprint | 任何可打印字符,包括图形字符和空白字符 |
| 是真就返回非0,假就返回0。 | |
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> #include <ctype.h> int main() { char ch = 'w'; int ret = islower(ch); printf("%d\n",ret); }
|
ch是小写字符,返回1
字符转换
使用需要引用头文件#include <ctype.h>
1 2
| int tolower(int c);//转小写 int toupper(int c);//转大写
|
1 2 3 4 5 6 7 8
| #include <stdio.h> #include <string.h #include <ctype.h> int main() { char ch = tolower('Q'); putchar(ch); }
|
Q被tolower函数转换成小写q,打印的结果是小写q
memcpy
void* mencpy (void * destination , const void * source ,size_t num);
内存拷贝,可以拷贝任意类型的变量。
C语言标准规定,memcpy只处理不重叠的拷贝,重叠的拷贝要使用memmove。(但有些编译器的memcpy也可以处理重叠拷贝)
strlen的局限性:
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { int arr1[] = {1,2,3,4,5}; int arr2[10] = {0}; return 0; }
|
使用memcpy:
1 2 3 4 5 6 7 8 9
| #include <stdio.h> #include <string.h> int main() { int arr1[] = {1,2,3,4,5}; int arr2[10] = {0}; memcpy(arr2, arr1, sizeof(arr1)); return 0; }
|
arr1的内容被拷贝到了arr2中去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <string.h> struct S { char name[20]; int age; };
int main() { int arr1[] = {1,2,3,4,5}; int arr2[10] = {0}; struct S arr3[] = {{"张三",20},{"李四",22}}; struct S arr4[3] = {0}; memcpy(arr2, arr1, sizeof(arr1)); memcpy(arr4, arr3, sizeof(arr3)); return 0; }
|
模拟实现memcpy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <stdio.h> #include <assert.h> struct S { char name[20]; int age; }; void* my_memcpy(void* dest,const void* src,size_t num) { void* ret = dest; assert(dest != NULL); assert(src != NULL); while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return ret; }
int main() { int arr1[] = {1,2,3,4,5}; int arr2[10] = {0}; struct S arr3[] = {{"张三",20},{"李四",22}}; struct S arr4[3] = {0}; my_memcpy(arr2, arr1, sizeof(arr1)); my_memcpy(arr4, arr3, sizeof(arr3)); return 0; }
|
memmove
void *memmove (void *dest,const void *src,size_t count);
用于处理mencpy不能处理的重叠拷贝。
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h> #include <string.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,10}; int i = 0; memmove(arr+2, arr, 20); for (i=0 ;i<10; i++) { printf("%d ",arr[i]); } return 0; }
|
打印的结果是:1 2 1 2 3 4 5 8 9 10
模拟实现memmove:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <assert.h> void* my_memmove(void* dest ,const void* src,size_t count) { void* ret = dest; assert(dest != NULL); assert(src != NULL); if (dest < src) { while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } }else { while (count--) { *((char*)dest+count) = *((char*)src+count); } } return ret; } int main() { int arr[] = {1,2,3,4,5,6,7,8,9,10}; my_memmove(arr,arr+2,20); return 0; }
|
memcmp
内存比较。
int memcmp (const void * ptr1 ,const void * ptr2 ,size_t num);
size_t num是比较字节的个数。
| 返回值 |
|---|
| stri1 < str2 | <0 |
str1 = str2
| 0 |
str1 > str2
| >0 |
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> #include <string.h> int main() { int arr1[] = {1,2,3,4,5}; int arr2[] = {1,2,5,4,3}; int ret = memcmp(arr1, arr2, 8); printf("%d\n",ret); return 0; }
|
比较前8字节,即1,2这两个整型。打印的结果是0
memset
修改字节。
void *memset(void *dest,int c,size_t count);
1 2 3 4 5 6 7 8
| #include <stdio.h> #include <string.h> int main() { char arr[10] = ""; memset(arr, '#',3); return 0; }
|
下面是一个错误示范:
1 2 3 4 5 6 7 8
| #include <stdio.h> #include <string.h> int main() { int arr[20] = {0}; memset(arr, 1, 10); return 0; }
|
memset(arr, 1, 10);不会让数组内的10个整型改为1,而是会让相邻的10个字节的内存改为1