本章前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串 中或者 字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数.
函数介绍
在学习函数我们会见到很多陌生的函数,怎么去认识这些函数我推荐一个网站C语言函数,废话少说,开始正题
strlen
在前面中我们已经使用过这个函数,知道这个函数的作用是计算字符串的长度,计算的是‘\0’前面的长度,不包括’\0’
参数是字符串起始的地址,也就是要从哪里开始计算字符长度的地址,返回的类型是无符号整形
#include<stdio.h> #include<string.h> int main() { char arr[] = "abcd"; char arr1[] = { 'a','b','c','d' };//这里没有'\0'; printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr1)); printf("%d\n", strlen("abcd")); return 0; }
其实我们还可以计算字符串常量的长度 strlen(“abcd”);传入的是a的地址
如果我们来分析一下对应的返回类型就会有另一个知识点
#include<stdio.h> #include<string.h> int main() { if (strlen("abc") - strlen("abcdef") > 0) { printf("大于"); } else { printf("小于"); } return 0; }
如果看过前面我写过的博客就会明白,无符号数的在内存的存储 ,所有都是有效位,连符号符号位也计算进去了 ,范围是0~4,294,967,295 ,上面虽然我们通过数学方法计算得到-3,但是却是无符号数,在内存的存储是
1111 1111 1111 1111 1111 1111 1111 1101
最终计算出来的是一个很大的数.
或者我们可以把-3强转成int类型,就会是真正的-3了
如果我们要模拟实现有三种方法
方法1:递归
#include<stdio.h> size_t my_strlen(char* arr) { if (!*arr) { return 0; } return 1 + my_strlen(arr + 1); } int main() { char arr[] = "abcdwe"; size_t a = my_strlen(arr); return 0; }
方法2:计数
#include<stdio.h> int main() { char arr[] = "aaaaaaaaa"; int a = 0; char* p = arr; while (*p++) { a++; } return 0; }
方法三:首元素的地址减去’\0’的地址
#include<stdio.h> int main() { char arr[] = "aaaaaaaaa"; char* p = arr; while (*p++) { ; } printf("%d", p - arr - 1); return 0; }
strcpy
字符串拷贝
返回类型是char* ,返回strDestination,我们可以认为这是一个初始地址, 参数有两个,一个是目的地址,一个源头地址,strSource是被拷贝的起始地址,strDestination是粘贴到的起始地址
trcpy函数将strSource(包括终止的null字符)复制到strDestination指定的位置。复制或附加字符串时不执行溢出检查。如果源字符串和目标字符串重叠,则strcpy的行为是未定义的。
会把’\0’也会拷贝过去的,
#include<stdio.h> #include<string.h> int main() { char arr[10] = "xxxxxxxxx"; strcpy(arr, "aaaa"); return 0; }
注意事项:
- 源字符串必须以 ‘\0’ 结束。
拷贝结束是要遇到源头的结束标志,如果源头没有结束标志,就会继续拷贝直到遇到结束标志
#include<string.h> int main() { char arr[] = { 'a','b','c','d' }; char arr1[] = "xxxxxxxx"; strcpy(arr1, arr); return 0; }
运行这段代码就会发现程序会崩溃,原因是没有遇见’\0’会一直拷贝,直到拷贝的个数超过了arr1数组的长度,越界访问,程序崩溃,
2… 会将源字符串中的 ‘\0’ 拷贝到目标空间。
3. 目标空间必须足够大,以确保能存放源字符串。
#include<stdio.h> #include<string.h> int main() { char arr[2] = "a"; char arr1[5] = "asbd"; strcpy(arr, arr1); return 0; }
这里的情况和上面的情况是一样的,会越界访问,程序崩溃,这里是主要是拷贝的长度太长
- 目标空间必须可变。
- 学会模拟实现。
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char* arr, const char* arr1) { assert(arr && arr1); char* p = arr; char* p1 = arr1; while (*p++ = *p1++) { ; } return arr; } int main() { char arr[] = "abcdhghgfg"; char arr1[] = "xxx"; my_strcpy(arr, arr1); return 0; }
strcat
字符串追加
这里的参数和strcpy的参数是一样的,有目标地址和源头地址
将源字符串的副本追加到目标字符串。目的地中的终止空字符被源的第一个字符覆盖,并且空字符被包括在由目的地中两者的串联形成的新字符串的末尾。
简单的理解就是源字符串的第一个字符会覆盖目标字符串的’\0’,然后往后添加字符,这里也会把源字符串的’\0’追加过来
#include<stdio.h> #include<string.h> int main() { char arr[] = "asfd"; char arr1[20] = "abcde"; strcat(arr1, arr); return 0; }
可以看到arr1的e后面原来是有结束标志的,因为字符串追加,会覆盖,
所以会有一些注意事项:
1.目标空间要足够大.可修改
#include<stdio.h> #include<string.h> #include<assert.h> int main() { char arr[] = "asfd"; char arr1[20] = "abcde"; strcat(arr, arr1); return 0; }
当我们运行出来就会发现当追加的字符串长度大于目标空间,就会追加就会发生越界访问,程序会崩溃
2. 目标字符串必须要有’\0’,源字符串也必须有’\0’
因为strcat追加是从目标字符串的’\0’开始追加的,如果没有就会无法追加,
如果源字符串没有’\0’就会一直追加
#include<stdio.h> #include<string.h> #include<assert.h> int main() { char arr[] = { 'a','b' }; char arr1[10] = "qqq"; strcat(arr1, arr); return 0; }
这里还是会发生越界访问,程序会崩溃
模拟实现
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char* arr1, const char* arr) { assert(arr1 && arr); char *p = arr; char* p1 = arr1; int sz = strlen(arr1); //找到目标的结束标志 p1 = p1 + sz; //数据追加 while (*p1 = *p) { p1++; p++; } return arr1; } int main() { char arr[] = "abc"; char arr1[10] = "xx\0x111"; my_strcat(arr1, arr); return 0; }
需要注意的是如果目标字符串的’\0’后面还有字符,会直接覆盖上去的,不会扩大字符长度,
到这里可能有些小可爱会像让字符串自己给自己追加,结果发现我们模拟出来的函数程序崩溃了
下面为例:
这里是长度位20的数组,数组里面有四个元素,我们追加是通过访问内存来获取对应的值,然后覆盖上去,当我们把‘\0’覆盖了,当源字符地址找到对应的内存,但是却不是\0’而是字符a, 下一个字符是b,如此往下,没有结束标记,一直追加,最终会发生越界访问,程序崩了
但是使用库函数strcat却可以,但是我们是不建议的,