前言
这篇文章是对于字符串操作函数、内存函数的比较详细的介绍。
我们都知道,字符串在C语言中使用的特别频繁,但类型里,却没有字符串这种类型,这时,众多的库函数就可以帮助我们灵活地使用字符串了
这篇文章同样适合已经对于字符串有初步了解的朋友,虽然本篇文章会由浅入深的介绍每个函数,并解释出每个函数是如何实现的,但还是建议您有一定的基础。
下图是本篇文章(共两篇)
话不多说,让我们正式开始吧
strlen
介绍
下图是函数的使用:
从a的地址向后,直到访问到\0结束
返回的是\0之前的元素个数
而当字符串中无\0之后,就会一直向后访问,直到遇到\0,
返回的是随机值
模拟实现strlen
共有三种写法,但这里只介绍第一种,对于剩下的两种,我会单独出一篇文章去讲解
#include<string.h> #include<stdio.h> #include<assert.h> int my_strlen(const char* str)//使用const是为了保证str指向的元素不被改变 { assert(str); int count = 0; while (*str != '\0')//\0的ASCII码值是0,所以此处可以直接写成while(*str) { count++; str++; } return count; } int main() { char arr = "abcdef"; int len = my_strlen(arr); printf("%zd\n", len); return 0; }
小测试
下面代码的运行结果是什么
int main() { if (strlen("abc") - strlen("abcdef") > 0) { printf("11\n"); } else { printf("00\n"); } return 0; }
答案
讲解
size_t strlen( const char * str );
这是strlen函数的定义
它的返回类型是size_t,那么问题肯定就出在这里了
那么接下来就了解一下size_t
我们转到定义之后,会发现size_t就是unsigned int:无符号整型
typedef unsigned __int64 size_t;
那么再回到这道题,
strlen("abc") == 3 strlen("abcdef") == 6 strlen("abc") - strlen("abcdef") == -3
-3 的类型是无符号整型,所以转换成补码是一个很大的数字,所以判断条件为真
提示:使用int或者size_t来定义strlen时,二者都可,
此处库函数返回值之所以定义成size_t类型,是因为数组的长度不可能是负数
size_t表明函数不会返回负数
int则更方便阅读和理解,二者都可以
strcpy
介绍:
strcpy(arr1, arr2);//arr1是目的地地址,arr2是源字符串,arr2必须以‘\0’结尾
把后者指向的内容,拷贝到前者指向的空间里
此处不可以用arr1 = arr2,把地址赋给地址这种操作,很奇怪
拷贝过程
注意:
arr2中的\0也必须要拷贝进arr1,如图:
模拟实现
实现过程
函数传参
实现拷贝
void my_strcpy(char* dest, const char* src) { assert(dest); assert(src); while (*src) { *dest++ = *src++; } *dest = *src; }
优化拷贝
my_strcpy(char* dest, const char* src) { assert(dest); assert(src); while (*dest++ = *src++) //表达式的结果是*src,而当*src为\0时,先赋值,再判断,判断结果为假,跳出循环,拷贝结束 { ; } }
返回类型
函数定义中,返回类型是char*
char* strcpy(char* destination, const char* source);
我们在函数完成拷贝后,还需要返回s1的首元素地址,来帮助我们找到拷贝之后的字符串,所以我们还需要单独创建一个变量来存储数组s1的首元素地址,来确保我们能找到这个数组。
最终代码:
char* my_strcpy(char* dest, const char* src) //目标地址发生变化,而源头地址不发生变化, //所以src前使用const,dest不使用const(要保证目标空间可改) { assert(dest); assert(src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[] = "abcdefghi"; char arr2[] = "hello"; char* ret = my_strcpy(arr1, arr2); printf("%s\n", arr1); }
strcat
函数定义
char* strcat(char* strDestination, const char* strSource);
作用:把arr2追加到arr1里,首先找到arr1中的\0,将其替换成arr2的首元素,之后向后追加,直到追加完\0结束。
错误示例如下:
int main() { char arr1[] = "hello"; char arr2[] = "world"; strcat(arr1, arr2); return 0; }
数组arr1空间不够大,直接越界访问,程序报错。
模拟实现
对于my_strcpy函数,需要实现两个功能:
首先需要找到目的字符串(arr1)中的\0
之后进行追加(拷贝)
找\0
while (*dest != '\0') { dest++; }
追加
while (*dest++ = *src++) { ; }
返回值
此处返回的仍应该是arr1的首元素地址,以便于我们找到字符串arr1
返回类型就是char*
完整代码
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest != '\0') { dest++; } while (*dest++ = *src++) { ; } return ret; }
小技巧
对于部分库函数,我们可以找到他们是如何实现的
具体操作方法如下:
找到对应的路径、找到要查看的头文件,直接用VS运行即可
这里以strcat为例子
这就是我们模拟实现中做的三步:
寻找\0
追加字符串
返回数组首元素地址
结语
因为文章长度,这篇文章只介绍三个函数,下篇文章会继续介绍