字符串,是一种由双引号引起的一整串字符,在C语言中,字符串是没有类型的,通常我们将字符串放在字符数组当中,同时,我们对于字符串的操作是很频繁的,因为对于字符串的操作频繁,所以C语言本身提供了一些对字符串进行处理的函数。
字符操作函数
strlen
strlen用于计算我们的字符串长度,对于strlen而言,是不会将结尾处的'\0'计算进去的,所以我们在用strlen的时候注意,参数的结尾一定要有'\0',不然strlen会返回一个随机值,对于我们的strlen来说,返回值一定是一个无符号的数,字符串的长度不可能出现负数,最小也是0,知道这些注意事项之后,我们进行模拟实现,因为我们知道strlen是计算'\0'之前出现的数,那我们就可以定义一个计数器,用循环语句,将'\0'作为我们的判断条件,每次进入的时候都将计数器加一次,同时我们的字符串也像后加加,当遇到'\0'的时候,不在进入循环,返回计数器的值。
#include<stdio.h> #include<string.h> #include<assert.h> size_t my_strlen(const char* ch) { assert(ch); size_t count = 0; while (*ch++) { count++; } return count; } int main() { char ch[] = "asdfghj"; int len1 = strlen(ch); int len2 = my_strlen(ch); printf("%d\n", len1); printf("%d\n", len2); return 0; }
strcpy
strcpy,字符串拷贝,是将我们想要拷贝的字符串拷贝到目标空间的一个函数,同样,对于我们想要拷贝的字符串,也是需要结尾处一定要有'\0'的,不然会在字符串后面带上一堆乱码,因为我们的strcpy也是以'\0'为判断条件,还有需要注意的是,我们要让目的地的空间放的下我们要拷贝进去的字符串,不然就会进行越界,目的地也必须是可变的,不能是常量字符串,那知道这些注意事项,我们进行模拟实现。
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char* ch1, const char* ch2) { char* cp = ch1; assert(ch1 && ch2); while (*ch1++=*ch2++) { ; } return cp; } int main() { char ch1[25] = { 0 }; char ch2[] = "asdfghjkl"; //strcpy(ch1, ch2); my_strcpy(ch1, ch2); printf("%s", ch1); return 0; }
strcat
strcat,字符串追加,可以将一个字符串的内容追加到另一个字符串的后面,这里要注意我们的两个字符串结尾处要有'\0',并且我们的目标空间要足够大,能放的下两个字符串才可以,并且我们的目的地要可以被修改,实现的话和我们的srecpy差不多,只需要提前找到我们目的地的'\0',然后进行追加。
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char* ch1, const char* ch2) { char* cp = ch1; assert(ch1 && ch2); while (*ch1++) { ; } ch1--; while (*ch1++ = *ch2++) { ; } return cp; } int main() { char ch1[20] = "hello "; char ch2[] = "world"; //strcat(ch1, ch2); my_strcat(ch1, ch2); printf("%s", ch1); }
那如果我们让它把自己的内容追加在自己的后面怎么办呢?那要自己追加自己,我们模拟实现的思路就行不通了,那我们优化一下,一开始记录下我们要追加在后面的字符串的地址,然后追加行不行呢?这样的话,目的地的字符串就变成了一个结尾没有'\0'的字符串,就会一直追加下去,那我们可不可以先记住要追加字符串的起始地址,然后用一个计数器,记住要追加的字符串有多少字符,追加完这些字符后,我们自己补一个'\0',是不是问题就解决了。
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char* ch1, const char* ch2) { char* cp = ch1; assert(ch1 && ch2); const char* c = ch2; int count = 0; while (*ch1++) { ; } ch1--; while (*ch2++) { count++; } while (count--) { *ch1++ = *c++; } *ch1 = '\0'; return cp; } int main() { char ch1[20] = "hello "; char ch2[] = "world"; //strcat(ch1, ch2); my_strcat(ch1, ch1); printf("%s", ch1); }
strcmp
strcmp,字符串比较,顾名思义,用来比较两个字符串的大小的,在C语言中,我们不能直接用大于小于号来确定字符串谁大谁小,但是本质上,字符串比较的是下标一样的字符的ASCII码值,如果大于则返回一个大于0的数,如果小于返回一个小于0的数,如果等于返回0,那知道这些我们就可以对其进行模拟实现。
#include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char* ch1, const char* ch2) { assert(ch1 && ch2); while (*ch1++ == *ch2++) { if (*ch2 == '\0') { return 0; } } ch1--; ch2--; return *ch1 - *ch2; } int main() { char ch1[] = "asdfghjkq"; char ch2[] = "asdfghjkl"; int len1 = strcmp(ch1, ch2); int len2 = my_strcmp(ch1, ch2); if (len1 > 0 && len2 > 0) { printf("ch1>ch2"); } else if (len1 == 0 && len2 == 0) { printf("ch1==ch2"); } else { printf("ch1<ch2"); } return 0; }
strstr
strstr,字符串查找,可以在一个字符串里面找另一个字符串,在实现strstr的时候,我们要注意,查找的时候可能会出现"dddf"里面找"ddf",当是这样的时候,我们找到"dd",如果让本身的地址进行加加,就会使让我们对比下一个字符串的时候对比的是'd'和'f',这样就算我们这个字符串包括另一个字符串也会返回空指针。
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strstr(const char* ch1, const char* ch2) { assert(ch1 && ch2); const char* cp1 = ch1; const char* cp2 = ch2; const char* p = ch1; while (*p) { cp2 = ch2; cp1 = p; while (*cp1!='\0'&&*cp2!='\0'&& * cp1 == *cp2) { cp1++; cp2++; } if (*ch2 == '\0') { return (char*)p; } p++; } return NULL; } int main() { char ch1[] = "asdfffghjsd"; char ch2[] = "ffh"; char* cp1 = strstr(ch1, ch2); char* cp2 = my_strstr(ch1, ch2); if (cp1 == NULL && cp2 == NULL) { printf("ch2不是ch1的字串"); } else { printf("ch2是ch1的字串"); } return 0; }
strtok
strtok,是一个特殊的函数,它可以将字符串分割,按照我们给出的分隔符分隔,我们进行第一次传参的时候,把字符串传过去,然后开始找分隔符,找到分隔符用'\0'代替,当要进行第二次分割的的时候,传参传空指针即可,当传参传空指针的时候,我们的strtok函数会找到上一次标记的地址,然后向后进行切割,如果在我们的字符串中,没有我们的分隔符,就会返回一个空指针。
strerror
strerror是一个用来报告错误的函数,它可以将错误码转换成错误信息,其中我们的错误码会保存在一个叫做errno的函数中,它需要引头文件<errno.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
任何可打印字符,包括图形字符和空白字符
字符转换
tolower 转换成小写字母
toupper 转换成大写字母
除了这些字符操作函数之外,还有一些,是在上面有些函数的基础上进行改造的,有strncpy、strncat、strncmp,这些函数都比原函数多了一个需要传的参数,这个参数限制我们要将多少个字符放到目的地或者进行比较,实现也和上面差不多,就不过多介绍了。除了我们这些能操作字符串的函数之外,还有一种函数,它比我们这种字符串的操作函数能应用的范围更加广泛一些,用于操作我们的内存。
内存操作函数
memcpy
memcpy,内存拷贝函数,它和strcpy的用处一样,传参是三个,第三个参数用于确定,我们要拷贝多少个字节到目的地,实现也与strlen大同小异。
#include<stdio.h> #include<string.h> #include<assert.h> void* my_memcpy(void* dest, const void* src, size_t byte) { void* p = dest; assert(dest && src); while (byte--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return p; } int main() { int arr1[20] = { 0 }; int arr2[] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; //memcpy(arr1, arr2, 20); my_memcpy(arr1, arr2, 20); for (; i < 5; i++) { printf("%d ", arr1[i]); } return 0; }
memmove
memmove对比上面的memcpy功能基本一致,但是memmove可以进行目的地和要拷贝的内容重叠在一起的时候,拷贝出来没有问题。
#include<stdio.h> #include<string.h> #include<assert.h> void* my_memmove(void* dest, const void* src, size_t byte) { void* p = dest; assert(dest && src); while (byte--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return p; } int main() { int arr1[20] = { 0 }; int arr2[] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; my_memmove(arr1, arr2, 20); for (; i < 5; i++) { printf("%d ", arr1[i]); } return 0; }
memcmp
memcmp,和我们的字符串比较函数一样,都是比较大小用的,它比较的值有一个范围,和strncmp是一样的,实现也差不多,不进行过多讲解。
总结
对于这些我们来说,熟练使用这些函数,在某种情况下可以更快的去实现我们的想法,所以要多运用这些函数。