C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串或字符数组中,字符串常量适用于那些对它不做修改的字符串函数。
求字符串长度:
1:strlen函数:size_t strlen(const char*str)【size_t=unsigned int】
举例:
#include<stdio.h> #include<string.h> int main() { int len = strlen("hello,C"); printf("%d\n", len); return 0; }
字符串已经’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含‘\0’),因此输出结果为6
6
但如果我们将其换一种写法,输出结果还会是6吗?
#include<stdio.h> #include<string.h> int main() { char arr[] = { 'h','e','l','l','o','C' }; int len = strlen(arr); printf("%d\n", len); return 0; }
**参数指向的字符串必须要以’\0’结束。
74
输出结果为什么是74呢?原因为,此时的hello,C,被我们存放在了字符数组而不是字符串,而字符数组并不包含’\0‘,但strlen函数只有遇到’\0’会停止计算长度,因此这种情况下,strlen函数会在随机值中继续查找,直到碰到’\0’,才会输出长度。
strlen函数返回的类型是size_t(unsigned int)
证明如下:
对上述示例中的stlen函数的返回值类型,我们进行了调整。
左边输出结果为hehe,原因:由于strlen函数的返回值类型是size_t(unsigned int),因此即使字符串”abc"和字符串“abcdef”相减,都不会返回-3,系统会将-3进行,按无符号整数输出。
右边的strlen函数的返回值类型是int,是有符号整数,因此函数返回值为-3.
2:使用指针:
#include<stdio.h> #include<assert.h> int my_strlen(const char* str)//const修饰指针char* //由于要求的字符串是常量字符串,因此指针指向的内容无法被改变 { assert(str != NULL);assert(断言):如果出现括号中的情况,则程序会停止运行 int count = 0; while (*str != 0) { count++; str++; } return count; } int main() { char arr[] =("abcdef"); int my_strlens = my_strlen(arr); printf("%d\n", my_strlens); }
6
注意:strlen函数的返回值是无符号整数,因此不会出现负数。
证明如下:
#include<stdio.h> #include<string.h> int main() { if ((strlen("abc") - strlen("abcdef")) > 0) printf("hehe");fan else printf("haha"); }
hehe
长度不受限制的字符串函数:strcpy/strcat/strcmp
strcpy:char* strcpy(destination,resource)
举例:
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char* dest,const char* res)//做字符串拷贝时,源头字符串不会发生改变变,可以用const修饰 { assert(dest != NULL); assert(res != NULL); char* ret = dest;//定义指针ret,指向dest while (*dest++ = *res++)//遇到'\0',循环终止 { ; } return ret;//注意:这里不能直接返回dest,因此此时的dest已经不是目地空间的起始地址了 } int main() { char arr1[] = "早上好呀"; char arr2[] = "小懒虫"; strcpy_s(arr1, arr2); my_strcpy(arr1,arr2); printf("%s\n", arr1); return 0; }
小懒虫
注意:*dest++ = res++,运算符和++都属于单目运算符,两者的优先级是一样的,结合性的方向是从右到左。
因此,有些小伙伴会认为这个表达式的运算顺序是:dest/res先自身+1,其次进行解引用,其实这种想法是错误的。
正确的运算顺序应该是:首先解引用指针变量dest/res,然后dest/res再自身加1,之所以不按照从右向左的结合性,是因为++自身的特性引起的。
使用strcpy函数时,需要注意的事项:
1:源字符串必须以’\0’结束
举例:
char arr[] = { 'h','e','l','l','o' };
如果源字符串是上述这种不包含’\0’的,编译器会一直往后找’\0’,因此很有可能b发生越界访问。
2:会将源字符串中的’\0’拷贝到目标空间
举例:
char arr[] = "bit";
arr其实有四个字符,其中还包含’\0’.
3:目标空间必须足够大,以确保源字符串能够完整放入
举例:
char arr[] = "bit"; char arr1[] = "hello,world";
如果是将arr1放入arr中,是不能实现的,因为arr只能存放4个字符,而arr1有12个字符
4:目标空间必须可变
char *p = "hello,world";
如果目标空间是如上所示这种,是无法将其它字符串拷入其中,因为此时p指向的是常量字符串,其无法被改变,如果强制性被改变,会导致程序崩溃。
strcat:
目标空间必须足够大:
举例:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "hello"; char arr2[] = "world"; strcat_s(arr1, arr2); printf("%s\n", arr1); return 0; }
如上所示代码:由于arr1的空间不够大,只能存放6个字符,因此将arr2拷贝到arr1的过程,会导致程序崩溃。
通过调试验证源字符串中的”\0‘也被拷贝过去了
使用strcat函数时,需要注意的事项:
1:源字符串必须以’\0’结束
2:目标空间必须足够大
3:目标空间必须可修改
以上三点,具体证明结果可参考上面的strcpy函数
4:字符串自己追加自己
证明如下:
#include<stdio.h> #include<assert.h> char* my_strcat(char* dest,const char* res) { assert(*dest!=NULL); assert(res); char* ret = dest; //先找目标字符串的结束标志 while (*dest != '\0') { dest++; } //字符串自己进行追加 while (*dest++ = *res++) { ; } return ret;//不能直接返回dest,因为此时的dest并不是目标空间的首地址 } int main() { char arr1[30] = "hello"; char arr2[] = "world"; my_strcat(arr1, arr2); printf("%s ", arr1); return 0; }
helloworld
通如下路径,我们可以查找各种库函数的实现方法:
strcmp:int strcmp(const charstr1,const charstr2)
举例:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "abcdef"; char arr2[] = "bbc"; //如果str1>str2,返回大于零的数字,str1<str2返回小于零的数字,二者相等返回0 int ret=strcmp(arr1, arr2); printf("%d ", ret); return 0; }
为什么输出结果是-1呢?如果strcmp比较的是字符串的长度,输出结果应该为大于0的数字
-1
由此我们可以得到结论:strcmp函数在比较字符串的时候,实际比较的是首元素的ASCII码值,如果首元素ASCII码相等,则依次比较第二个第三个,直到不相等。
标准规定: 如果str1>str2,返回大于零的数,str1<str2返回小于零的数,二者相等返回0
在VS编译器下,如果str1>str2,返回1,str1<str2返回-1,二者相等返回0
而在Linux-gcc系统下,strcmp函数返回的是ASCII的差值
因此在使用strcmp函数的时候,不能将函数的返回值设置为具有局限的:
举例:
#include<stdio.h> #include<string.h> int main() { char arr1[] = "abcdef"; char arr2[] = "bbc"; //1,-1,0只是在VS编译器下,strcmpy函数返回的结果,但C语言并为这样定义 if (strcmp(arr1, arr2) == 1) { printf("str1>str2"); } else if (strcmp(arr1, arr2) == 0) { printf("str1=str2"); } else if (strcmp(arr1, arr2) == -1) { printf("str1<str2"); } return 0; }
正确写法:
if (strcmp(arr1, arr2)>0) { printf("str1>str2"); } else if (strcmp(arr1, arr2) == 0) { printf("str1=str2"); } else if (strcmp(arr1, arr2) <0) { printf("str1<str2"); }
用库函数定义的方法进行实现:
#include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char*str1,const char*str2)//字符串只是进行比较,并会改变值,因此可以用const修饰 { assert(str1 && str2); while (*str1 == *str2) { if (*str1 && *str2 == '\0')//字符串相等 { return 0; } str1++; str2++; } return (*str1 - *str2); } int main() { char arr1[] = "abcsdf"; char arr2[] = "nihao"; int ret=my_strcmp(arr1, arr2); printf("%d", ret); return 0; }
-13
上述所讲的strcpy/strcat/strcmp函数,都是不受长度限制的函数,因此特们不太安全,有时会强制性的进行字符串操作。因此我们引出下受长度限制的字符串函数:
长度受限制的字符串函数:strncpy/strncat/strncmp
strncpy:charstrncpy(chardest,onst char*res,size_t count(单位是字节))
拷贝num个字符从源字符串到目标空间,如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个
举例:
int main() { char arr1[30] = "abcdef"; char arr2[] = "bit"; strncpy_s(arr1, arr2, 8); printf("%s ", arr1); return 0; }
bit
strncat:charstrncat(chardest,const *strres,size_t count)
指定长度的追加,追加完毕后,给后面加\0
举例:
#include<stdio.h> #include<string.h> int main() { char arr1[30] = "hello"; char arr2[] = "world"; strncat_s(arr1, arr2, 3); printf("%s\n", arr1); return 0; }
hellowor
strncmp:int strcmp(const charstr1,const charstr2,size_t num)
标准规定: 第一个字符串大于第二个字符串,则返回大于零的数字
第一个字符串小于第二个字符串,则返回小于零的数字
两个字符串相等,返回0
举例:
int main() { char arr1[30] = "abblo"; char arr2[] = "abbld"; int ret = strncmp(arr1, arr2, 4);//第三个参数为比较的字节数 printf("%d ", ret); return 0; }
strncmp函数和strcmp函数不同的是,该函数对所比较的字符长度进行了限制,如上述所示arr1(abblo)和str2(abbld),前四个元素的ASCII码是相同的,但由于我们对二者所比较的长度进行了限制,因此,第五个元素并不会进行比较,所以输出结果为0
0