C语言中,本身没有字符串类型的,字符串通常以字符数组和常量字符串的形式出现。
而有一些库函数可以对字符串进行操作,使我们对字符串的处理可以简单许多,但是注意的是:这些库函数不可以修改常量字符串
1.求字符串长度
strlen size_t strlen ( const char * str );
strlen函数计算的是字符串中'\0'前面出现的字符个数,不包含'\0'
参数指向的字符串一定要以'\0'结尾,否则会计算出随机值
strlen是求字符串长度的,求出的长度是不可能为负数,所以返回size_t类型的值,size_t其实就是无符号的整形unsigned int
由于strlen返回无符号整形,所以这里是一个易错点,以接下来的代码为例
#include <stdio.h> #include <string.h> int main() { char str1[] = "abcdefg"; char str2[] = "abcdefghijk"; if (strlen(str1) - strlen(str2) < 0) printf("str1长度小于str2长度"); else printf("str1长度大于str2长度"); return 0; }
str1长度为7,str2长度为11,7-11 = -3,但是这里的7和11是无符号整形,他们俩相减会得到一个非常大的数,而不是-3,所以这个程序会输出str1长度大于str2长度
模拟实现strlen
这里有三种方法进行模拟实现
第一种:常规方法:
#include <stdio.h> #include<assert.h> #include <string.h> int my_strlen1(const char* str) { assert(str != NULL); int count = 0; while (*str != '\0') { count++; str++; } return count; }
第二种:递归:
#include <stdio.h> #include<assert.h> #include <string.h> int my_strlen2(const char* str) { assert(str != NULL); if (*str != '\0') { return 1 + my_strlen2(str+1); } else { return 0; } }
第三种:指针相减:
#include <stdio.h> #include<assert.h> #include <string.h> int my_strlen3(const char* str) { assert(str != NULL); const char* start = str; while (*str != '/0') { str++; } return str - start; }
两个指向同一块空间的两个指针,两个指针相减得到这两个指针间的元素的个数
2.长度不受限制的字符串函数
strcpy char* strcpy(char * destination, const char * source );
1
这个函数的作用是将source中的字符串拷贝到空间destination中
destination是目标空间,函数将复制的字符串放到这个目标空间中
source是源字符串,函数会复制源字符串,因为只是对源字符串进行复制,并不会改变它,所以可以将源字符串写成const
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
源字符串必须以 ‘\0’ 结束,因为源字符串读到\0就停止拷贝。
函数会将源字符串中的 ‘\0’ 拷贝到目标空间。
strcpy的使用:
#include <stdio.h> #include <string.h> int main() { char arr1[20] = {0}; char arr2[] = "abcdef"; strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
如果在源字符串中提前放一个\0,那么函数只会拷贝到\0
这里可以看到,在arr2中ab后面放了一个\0,则在函数拷贝时只会拷贝ab\0
我们可以调试函数看一下:拷贝前的arr1:
拷贝后的arr1:
可以看到,只拷贝了ab\0
模拟实现strcpy
最常规写法:
#include <stdio.h> #include<assert.h> #include <string.h> char* my_strcpy(char* destination, const char* source) { char* ret = destination; assert(destination && source ); while (*source!='\0') { *destination = *source; destination++; source++; } *destination = *source;//拷贝\0 return ret; }
这么要注意的一点就是,strcpy拷贝过程中会源字符串中最后的\0,但是如果以while (*source!='\0')为循环条件的话,最后的\0就不会被拷贝
所以要在循环外部额外再拷贝一次*destination = *source;
简化一点:
#include <stdio.h> #include<assert.h> #include <string.h> char* my_strcpy(char* destination, char* source) { char* ret = destination; assert(destination && source ); while (*src != '\0') { *destination++ = *source++; } *destination = *source;//拷贝\0 return ret; }
再简化:
前面的两种写法都需要在循环外部额外再对\0进行拷贝,而下面的写法直接将\0的拷贝也放到了循环中进行
#include <stdio.h> #include<assert.h> #include <string.h> char* my_strcpy(char* destination,const char* source) { char* ret = destination; assert(destination && source ); while (*destination++ = *source++) { ; } return ret; } 1
strcat char * strcat ( char * destination, const char * source );
1
这个函数的作用是将source中的字符串追加到destination的后面
目标空间要有\0结尾,因为要直到从哪开始追加
源字符串要有\0结尾,因为要知道何时结束追加
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
strcat的使用:
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "hello"; char arr2[] = " world"; strcat(arr1, " world"); printf("%s", arr1); return 0; }
当一个字符串,自己对自己追加的时候会有问题:
以字符串abcdef追加自己为例
从\0开始追加,a追加到f,本来f后面是\0,到了\0就停止追加,但是前面追加时已经就将\0覆盖了,所以不会停止
所以当一个字符串自己追加自己,会发生死循环
模拟实现strcat
#include <stdio.h> #include<assert.h> #include <string.h> char* my_strcat(char* des, char* src) { assert(des && src); char* ret = des; while (*des != '\0') { des++; } while (*des++ = *src++) { ; } return ret; }
strcmp int strcmp ( const char * str1, const char * str2 );
1
标准规定(在非VS环境中):
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
而在VS环境中:
第一个字符串大于第二个字符串,则返回1
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回-1
对于两个字符串的大小,比较的并不是字符串的长度,而是一个一个比较组成字符串的字符,字符对应ASCII码值大的字符大。例如:'z'大于'a'
模拟实现strcmp
#include <stdio.h> #include<assert.h> #include <string.h> int my_strcmp(char* str1,char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') { return 0; } str1++; str2++; } //VS环境下: if (*str1 > *str2) return 1; else return -1; //非VS环境下: //return *str1-*str2; }