适合初学者的字符函数和字符串函数(学不会打我)上 (一)

简介: 适合初学者的字符函数和字符串函数(学不会打我)

长度不受限制的字符串函数


strlen部分


strlen函数的易错小知识


我们知道,strlen函数是用来求字符串的长度的。


size_t strlen( const char *string );


Each of these functions returns the number of characters in string, excluding the terminal NULL.


其中每个函数都返回字符串中的字符数,到\0为止(不包括\0),不包括NULL指针。


需要注意的是,strlen的返回类型是size_t.


size_t 是一些C/C++标准在stddef.h中定义的,size_t 类型表示C中任何对象所能达到的最大长度,它是无符号整数。


初学者很容易误以为strlen的返回类型是int,这点需要特别注意,看下面的代码来巩固。


#include <stdio.h>
#include<string.h>
int main()
{
  const char* str1 = "chendada";
  const char* str2 = "chen";
  if (strlen(str2) - strlen(str1) > 0)
  {
  printf("str2>str1\n");
  }
  else
  {
  printf("srt1>str2\n");
  }
  return 0;
}


运行结果如下:


da96dfaddc75f20ab8a88161faab9025_c1a8dc4d60a04ee89928062aad239c13.png


嘻嘻,不知道有多少老铁踩坑了?😋可不能想当然地认为结果是个负数,切记strlen的返回类型是无符号整形!


这两个字符串长度相减的结果,如果从整形的角度来看,是-4没错,但是我们的返回类型是无符号整形,而无符号整形减去无符号整形的结果也是一个无符号整形。


也就是说,-4储存在内存中的补码并没有转换成源码来参与运算,而是直接被当成是一个数字来参与运算,我们知道,这个运算的结果一定是一个正数。


strlen函数的实现


strlen函数的实现更是多种多样,我在这里列出以下三种。


1.计数器


2.递归


3.指针-指针


//计数器方式
int my_strlen(const char * str)
{
 int count = 0;
 while(*str)
 {
 count++;
 str++;
 }
 return count;
}
//递归,不需要创建临时变量计数器
int my_strlen(const char * str)
{
 if(*str == '\0')
 return 0;
 else
 return 1+my_strlen(str+1);
}
//指针-指针的方式
int my_strlen(char* str)
{
  char* p = str;
  while (*p != '\0')
  {
  p++;
  }
  return p - str;
}

我们在这里将返回类型改成int,这样可以有效规避上面无符号整形导致误判的情况。


在以上代码里,我们都可以加入const和assert断言函数来让程序更安全。


strcpy部分


函数的参数形式char* strcpy(char*destination,const char*source);


该参数说明了strcpy返回类型是char类型的指针,将源头(不能被改)拷贝到目的地。


strcpy特点和strlen类似,遇到‘\0’就停止。


看下面的代码,最后打印的是什么呢?


#include <stdio.h>
#include<string.h>}
int main()
{
    char str1[100] = "chen\0dada";
    char str2[100] = "xxxxxxxxx";
  strcpy(str2, str1);
  printf("%s", str2);
  return 0;
}

运行结果如下:


63b9abac3192986a62fd5f6b7e00d7ba_436f0d9b982f4ca8a438eec69d78c501.png


正如上面所说,遇到\0就停止。


34568880047417ec23cdbe515d97d3e9_34e8fe5150ef45ccacb38e03745c93f3.png


可以看到,strcpy会将\0也一并拷过去。


需要特别注意的是,目标空间必须可变,且必须足够大,用来确保能存放源字符串。


自己实现strcpy的方式非常多,在自制strcpy函数之前,我们需要知道,strcpy函数返回的是目标空间的起始地址,所以它的返回类型是一个指针,例如char *。


我们知道源头的地址不能改变,所以我们要加上const来修饰。


同时,为了确保指针有效,我们使用断言函数。


char* my_strcpy(char* dest, const char* src)
{
  char* ret = dest;//目标空间的起始地址
  assert(dest && src);
  while ((*dest++ = *src++))
  {
  ;
  }
  return ret;
}

strcpy函数返回的是目标空间的起始地址,所以它的返回类型是一个指针,例如char *。


我们可以用这一点实现下面的代码。


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
  char* ret = dest;
  assert(dest != NULL);
  while ((*dest++ = *src++))
  {
  ;
  }
  return ret;
}
int main()
{
  char a[10] = "";
  char b[] = "dada";
  printf("%s", my_strcpy(a, b));
  return 0;
}


d631a9811f4fb25d716f0169ccc2706c_08859f64371b4f9394746031c2c3011a.png


strcat部分


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

Appends a copy of the source string to the destination string. The terminating null character

in destination is overwritten by the first character of source, and a null-character is included

at the end of the new string formed by the concatenation of both in destination.

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

目标空间必须有足够的大,能容纳下源字符串的内容。

目标空间必须可修改。

简单的例子如下:

#include <string.h>
#include <stdio.h>
void main( void )
{
   char string[80];
   strcpy( string, "Hello world from " );
   strcat( string, "strcpy " );
   strcat( string, "and " );
   strcat( string, "strcat!" );
   printf( "String = %s\n", string );
}


10110baf5adfe76f29da121750fecd9a_d9607d86755942ea84e06c16ce5a7389.png


strcat函数是从\0的位置开始,会将\0用第一个元素覆盖。


那strcat能不能将自己拷贝到自己后面,即函数的参数为同一字符串呢?


我们看下面的代码。


自己实现strcat


char* my_strcat(char* dest, const char* src)
{
  char* ret = dest;
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest)
  {
  dest++;
  }
  while ((*dest++ = *src++))
  {
  ;
  }
  return ret;
}


答案自然是不行的,调试会一直卡在这个位置,程序是死循环的结果。


为什么会死循环呢?请看我做的图。


一开始是这样,dest和src全都指向第一个字符。


56908d05e9650e7147fc9364b348ee27_990e084bded94aa3b5a065efda086054.png


然后dest找到\0的位置,准备开始拷贝。


2e4d70818463c46f8ee480a60e995370_a0760b8af3ea47459ecc39a4612f5c8e.png


紧接着\0被转化为c,且src向后位移一个字节。


86cfa9853b26e85511a45678acb99bba_03c57cc6d4a74bd4811fcb6528f0bb65.png


这样下去的结果是。


7a250eab963603eb3276123a2be025a6_7e1747e33ea74659a9ad1cd98d39e02b.png


dest应该去找下一个\0,可是\0已经在上面被字符'C'覆盖,也就是说,\0已经不复存在,程序崩溃。


相关文章
|
6月前
|
C语言
C语言:字符函数和字符串函数(一篇拿捏字符串函数!)
C语言:字符函数和字符串函数(一篇拿捏字符串函数!)
65 0
|
12月前
|
C语言
【C语言】字符函数和字符串函数详解
【C语言】字符函数和字符串函数详解
|
1月前
|
C语言
C语言常见字符函数和字符串函数精讲
C语言常见字符函数和字符串函数精讲
|
5月前
|
编译器 C语言 C++
【C语言基础】:字符函数和字符串函数-2
【C语言基础】:字符函数和字符串函数
|
5月前
|
C语言
【C语言基础】:字符函数和字符串函数-1
【C语言基础】:字符函数和字符串函数
|
6月前
|
安全 Serverless C语言
C语言(10)----字符函数和字符串函数
C语言(10)----字符函数和字符串函数
23 0
|
6月前
|
C语言 存储 编解码
C语言(进阶)—字符函数和字符串函数
C语言(进阶)—字符函数和字符串函数
|
C语言
C语言学习系列-->字符函数和字符串函数
C语言学习系列-->字符函数和字符串函数
53 0
|
C语言
C语言进阶字符函数和字符串函数(上)
C语言进阶字符函数和字符串函数(上)
69 0
|
C语言
C语言进阶字符函数和字符串函数(下)
C语言进阶字符函数和字符串函数(下)
38 0