> 文档中心 > C字符串库函数(一)【自用】

C字符串库函数(一)【自用】

文章目录

  • 1. strlen()
    • strlen()返回不带'\0'的字符个数
    • 参数指向的字符串必须以'\0'结束
    • 函数的返回类型是size_t,不是int
    • 模拟strlen()函数
      • 🍎 非递归方法
      • 🍎 递归方法
      • 🍎 指针-指针
  • 2. strcpy()
    • 源字符串必须以`'\0'`结束
    • 会将源字符串中的`'\0'`拷贝到目标空间
    • 目标空间必须足够大
    • 目标空间必须可变
    • 模拟strcpy()函数
      • 🍎 常规方法
      • 🍎 改进
  • 3. strcat()
    • strcat()从目标字符串`'\0'`处开始追加,所以源字符串里必须有`'\0'`
    • 模拟strcat()函数
  • 4. strcmp()
    • strcmp函数的比较原理
    • strcmp的返回值为>0,<0或0,具体的值视编译器而定
    • 模拟strcmp函数
    • 字符串直接用>. <比较,比较的是什么?
  • 5. strstr()
    • 模拟strstr函数

1. strlen()

size_t strlen ( const char* str );

  • 函数返回的是字符串不含\0的字符个数
  • 参数指向的字符串必须要以\0结束
  • 函数的返回类型是size_t,无符号整型,不是int

strlen()返回不带’\0’的字符个数

char str[] = "abcde";int num = strlen(str);

num的值是5。

其实str数组的组成是这样的。它有6个元素,最后一个元素'\0'我们看不到,而strlen也不会计算这个'\0'

请添加图片描述

参数指向的字符串必须以’\0’结束

不含'\0'会出错的
请添加图片描述

函数的返回类型是size_t,不是int

其中第三点是一个易错点。strlen返回的起始不是int型,而是无符号整型unsigned long

在我的编译器中可以看到,这段程序可以运行起来,但是有报错,不能用%d,而是%lu

平常的应用中可能无伤大雅,但是我们来看这个例子

int main(){ if(strlen("abc") - strlen("abcdef") > 0)    { printf(">\n");    }    else    { printf("<=\n");    }    return 0;}

"abc"是3个字符,"abcdef"是6个字符,3-6 = -3,应该输出<=

然而实际上呢

)

因为strlen(“abc”)返回的是 无符号的3

strlen(“abcedf”)返回的是 无符号的6

最后的结果-3如果按照无符号数来算将是一个很大的数,else语句永远不会被执行

这里我们就要改进一下,进行强制类型转化才能得到想要的

模拟strlen()函数

🍎 非递归方法

size_t my_strlen(const char* str){  int count = 0;  while(*str != '\0')  {    count++;    str++;  }  return count;}

🍎 递归方法

size_t my_strlen(const char* str){  while(*str != '\0')  {    return my_strlen(str + 1) + 1;  }  return 0;}

🍎 指针-指针

size_t my_strlen(const char* str){  char* start = str;  while(*str != '\0')  {    str++;  }  return (str-start)/sizeof(char);}

2. strcpy()

char* strcpy( char* destination , const char* source );

  • 将source指向空间中包括’\0’在内的字符串拷贝到destination所指空间中
  • 源字符串必须以'\0'结束
  • 会将源字符串中的'\0'拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串
  • 目标空间必须可变
  • 返回目标空间的起始地址

源字符串必须以'\0'结束

int main(){ char arr1[20] = {0};    char arr2[] = {'a','b','c'};//应改为{'a','b','c','\0'}    strcpy(arr1,arr2);    return 0;}

arr2没有'\0',这段代码会让程序崩溃

会将源字符串中的'\0'拷贝到目标空间

int main(){    char arr1[20] = "XXXXXXXX";    char arr2[] = "abcde";    strcpy(arr1,arr2);    printf("%s\n",arr1);}

比如这段代码,最开始arr1和arr2数组是这样的

而strcpy语句执行后

可以看到’\0’也被拷贝进了arr1里

目标空间必须足够大

arr1不够大,报错力~。因为arr2有4个元素,arr1[2]是肯定不够的

在有的编译器中(比如vs),目标空间如果没有源字符串空间大也是能强行塞进去的,不过程序仍然是崩溃的,因为arr1的栈空间被破坏了,所以作为程序员,我们必须让目标空间足够大!

目标空间必须可变

int main(){ char* arr1 = "Atsuki";    char arr2[] = {'a','b','c','\0'};    strcpy(arr1,arr2);    printf("%s\n", arr1);    return 0;}

这样一段代码程序也是崩溃的,因为arr1是字符指针,字符指针指向的是常量字符串,不可变~

模拟strcpy()函数

🍎 常规方法

//返回类型是char*,因为函数返回值是目标空间起始地址char* my_strcpy(char* dest, const char* src){  char* ret = dest;//保存目标空间的起始地址作返回值,因为后面dest就变了  while(*src != '\0')  {    *dest = *src;    dest++;    src++;  }  *dest = *src;//当*src为'\0'时,循环已经结束,'\0'还未拷贝过去,因此有了这一句  return ret;}

🍎 改进

char* my_strcpy(char* dest,const char* src){  char* ret = dest;  while(*dest++ = *src++)  {    ;  }  return ret;}//这段代码妙在循环条件,改进前'\0'是在循环外赋值的,这个方法直接达到了循环完成所有拷贝//赋值表达式的值是右侧表达式的值//当*src == '\0'时,*dest = *src这个表达式的值为0,循环结束

3. strcat()

char* strcat ( char* destination, const char* source) ;

  • 在目标字符串后追加源字符串的拷贝。目标字符串的'\0'被源字符串的第一个字符覆盖,形成的新字符串的末尾包括'\0'
  • 源字符串必须以'\0'结束
  • 目标空间必须足够大,能容纳下源字符串的内容
  • 目标空间必须可修改

strcat()从目标字符串'\0'处开始追加,所以源字符串里必须有'\0'

最后arr1里A覆盖了原本的'\0',但是这个数组放不下拼接后的数组,所以目标空间一定要主动设定到足够大

如果我们让字符串里本来就有'\0'呢?

源字符串的拷贝从第一个\0就开始插入了。

模拟strcat()函数

char* my_strcat(char* dest, char* src){    assert(dest && src);    char* ret = dest;    //找目标空间的'\0'    while(*dest )    { dest ++;    }    //拷贝    while((*dest++ = *src++))    { ;    }    return ret;}

4. strcmp()

int strcmp( const char* str1, const char* str2);

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

strcmp函数的比较原理

strcmp函数比较的不是字符串长度,而是比较字符串对应位置上的字符的大小(ascii码值),如果相同,就比较下一对,直到不同或者都遇到\0

ret1,一直比较到arr1的a与arr2的'\0'比较,最终得出arr1大,正数

ret2,比较到arr2的s和arr3的a,最终得出arr2大,正数

ret3,和ret2一样, 但是ts小,所以得出一个负数

strcmp的返回值为>0,<0或0,具体的值视编译器而定

我用的sublime返回的就是两个字符ascii码相减的值

而如果是vs就会返回1,-1

模拟strcmp函数

int my_strcmp(const char* s1, const char* s2){    assert(s1 && s2);    while(*s1 == *s2)    { if(*s1 == '\0') {     return 0;//相等 } s1++; s2++;    }    return *s1 - *s2;}

字符串直接用>. <比较,比较的是什么?

是地址

char arr1[] = "abcde";char arr2[] = "abc";if(arr1 < arr2){  //数组名是数组首元素地址,所以这段代码比较的是两个数组的地址}if("abc" < "abcde"){  //字符串也有自己的地址,是字符串首字母的地址}

5. strstr()

char* strstr( const char* str1, const char* str2 );

  • 判断str2是不是str1的子串
  • 返回str2第一次出现在str1中的首字母地址,如果str2不是str1的子串返回空指针
char str1[] = "abcdefgcdeg";char str2[] = "cde";char* ret = strstr(str1,str2);

比如这段代码,str1中有两个"cde",strstr返回的就是第一次出现的cde的c的地址

请添加图片描述
看这个结果。因为ret是第一个cde的首字母地址,所以%s顺着这个地址把它后面的字符都打印出来了。

模拟strstr函数

char* my_strstr(char* str1,char* str2){    assert(str1 && str2);    char* s1 = str1;    char* s2 = str2;    char* cur = str1;    while(*cur)    { s1 = cur; s2 = str2; while(*s1 && *s2 && (*s1 == *s2)) {     s1++;     s2++; } if(*s2 == '\0') {     return cur; } cur++;    }    return NULL;}