征服C语言字符串函数(超详细讲解,干货满满)(上)

简介: 征服C语言字符串函数(超详细讲解,干货满满)

一.字符串函数

1.1.strlen

1.1.1.strlen剖析

strlen:求字符串长度函数

1.首先,我们先看一下strlen这个函数的返回值和参数类型

size_t strlen ( const char * str );
C语言中VS编译器下是这样定义size_t的:
typedef unsigned __int64 size_t;
也就是说:size_t实际上是对unsigned int类型起的一个别名,
我们可以理解为size_t就是一种unsigned int
其中的const char* str:说明传入的参数需要是一个地址,
strlen函数通过所传入的一个地址来找到所对应的字符串,
然后从对应地址处开始读取计算字符个数,一直计算到'\0'为止.
!!!注意:
1.strlen函数是计算'\0'之前的字符个数,
也就是说'\0'本身并不会计算在strlen所计算的个数之内
2.sizeof操作符计算字节大小时也会计算上'\0'
3."abcd"这种类型的字符数组在末尾处会隐含'\0',
而{'a','b','c','d'}这种类型的字符数组在末尾处则不会隐含'\0'
也就是说除非在末尾处人工添加'\0',否则这种字符数组末尾是没有'\0'的
4.关于const的作用是:限制str所指向的内容不能被修改,
也就是*str不能被修改,即不能充当左值
例如:后面给了大家答案,大家可以参照一下
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = { 'a','b','c','d','e','f' };
  printf("arr1的strlen:%d\n", strlen(arr1));//6
  printf("arr1的sizeof:%d\n", sizeof(arr1));//7
  printf("arr2的strlen:%d\n", strlen(arr2));//x
  printf("arr2的sizeof:%d\n", sizeof(arr2));//6
  return 0;
}

1.1.2 模拟实现strlen函数

接下来让我们模拟实现一下strlen函数,

1.在这里我们使用int类型作为返回值,实现完后会跟大家说明原因

2.在这里我们使用assert宏来保证arr所指向的空间不是NULL,

便于函数调用者传入参数错误时能够精确定位到错误的位置和原因

3.assert宏会在后面给大家详细说明

法一:计数器方法

思想是这样的:

让指针从数组首元素开始一次右移读取字符,

记录下读取到的字符个数,即为’\0’之前的字符个数

从strlen函数的功能上面可以很好的理解

//法一:计数器
int my_strlen1(const char* arr)
{
  assert(arr != NULL);
  int count = 0;
  while (*arr++ != '\0')
  {
    count++;
  }
  //注意:这两种对arr++的操作完全相同,
  //大家可以思考一下为什么这两种方法得到的最终答案相同
  //while (*arr != '\0')
  //{
  //  count++;
  //  arr++;
  //}
  return count;
}

下面我们解释一下为什么这两种方法得到的最终答案相同

1.注意:两种while循环的循环次数是相等的

但是不同点在于

1.第一种while循环中arr在循环条件判断结束之后就进行了自增操作,

2.而第二种while循环中arr的自增操作是在循环体内部完成的

不过这个函数返回的是count,而count只与while循环的循环次数有关,与arr无关,所以最终答案相同

2.递归方法

与上面的具体实现方式相同,只不过算法实现方式并不相同

上面是计数器方式,这里是递归调用的方式

这里的递归思想是

把一个字符串(假设长度为len)分割为

1.第一个字符

2.后面那个长度为len-1的字符串

第一步:

我们判断第一部分是否为’\0’,

如果为’\0’,则终止计算,返回0

第二步:

如果不为’\0’,则返回1+计算第二部分的长度

//法二:递归
int my_strlen2(const char* arr)
{
  assert(arr != NULL);
  if (*arr == '\0')
  {
    return 0;
  }
  return 1 + my_strlen2(arr + 1);
}

3,指针-指针的方法:

补充:

指针-指针:得到的是两个指针之间的元素个数!!!

指针之间可以进行减法(前提:必须是指向同一结构的指针,例如;数组)

指针之间不可以进行加法

思路:

让end指针指向’\0’的位置,然后只需要返回end-arr即可

//法三:指针-指针
int my_strlen3(const char* arr)
{
  assert(arr != NULL);
  char* end = arr;
  while (*end != '\0')
  {
    end++;
  }
  //下面的方法是错误的,得到的字符串长度为实际长度+1
  //大家可以思考一下为什么这两种方法得到的最终答案不同
  //while (*end++ != '\0')
  //{
  //  ;
  //}
  return end - arr;
}

前面说明了这两种while循环的区别之处和相同之处

下面我们来看一下这个题为什么得到的答案就不一样了呢?

1.这题返回的是end-arr,又因为arr在该函数内部并不会变动

所以最终答案与end有直接联系

2.上面提到过end是在while()循环的条件判断出进行的,

而我们又知道:

在for循环和while循环中,循环体执行的次数是循环条件执行的次数-1,所以错误的方法中end多自增了一次,所以返回的答案是实际答案+1

下面我们说一下用int类型作为返回值的好处
大家可以做一下这道题
int main()
{
  if (strlen("abc") - strlen("abcdef") > 0)
  {
    printf(">\n");
  }
  else
  {
    printf("<=\n");
  }
  return 0;
}
最终的答案是   > ,而不是 <=
其实最终的答案是挺出人意料的,
因为strlen这个库函数的返回值为size_t(可以理解为unsigned int类型)
所以最终得到的"-3"是size_t类型,而不是int类型,
所以"-3"在内存中被取出时是以无符号整型的视角去取出的,所以-3被取出时是一个非常大的正数,所以答案是>
所以使用size_t作为返回值的话无法用来这样直接判断两个字符串的长度,
不太方便,也正是因为这个原因我们使用了int类型作为返回值,
这样的话就可以很方便的直接判断两个字符串的长度了
那是不是说库函数实现的strlen函数用size_t函数作为返回值就不好呢?
当然不是,strlen这个函数设计的本意就是求字符串的长度
显然结果不可能为负数,所以我们使用了size_t作为返回值是很好的
这也就说明了不同的人在设计相同功能的函数时,出发的角度不同,所设计出的函数也会不同,也就是说,这两种设计方式各有各的好处,我们要灵活使用
下面我们说一下assert这个宏
void assert (int expression);
assert翻译过来就是断言的意思,
也就是说assert会执行expression中的语句,
如果这个语句的计算结果为0,那么断言失败,程序终止,
并且在屏幕上指出错误位置和具体原因
我们可以把assert中的语句和if中的语句联系起来看待,
例如:
int main()
{
  int a = 2;
  int b = 0;
  if (b = a)
  {
    printf("you can see me\n");//会执行该条语句
  }
  printf("%d", b);//2
}
举一个例子,没有什么意义,只是为了说明assert中的语句会执行
例如:
int main()
{
  int a = 2;
  assert(a=1);
  printf("%d", a);//结果为1,说明assert中的语句会执行
}

1.2 strcpy

strcpy:字符串拷贝函数

1.2.1 strcpy函数剖析

strcpy(字符串拷贝)
 char* strcpy(char* destination,char* source)
英语好的老铁可以看一下:
Copies the C string pointed by source into the array pointed by destination, 
including the terminating null character(and stopping at that point).
翻译后:
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。

1.2.2 strcpy函数模拟实现

char* my_strcpy(char* dest,const char* src)
{
  char* ret = dest;
  assert(dest != NULL);
  assert(src != NULL);
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}
int main()
{
  char arr1[20] = "xxxxxxxxxxxxxxx";
  char arr2[] = "hello world";
  my_strcpy(arr1, arr2); //arr2中'\0'及'\0'之前的元素拷贝到arr1中
  printf("%s\n", arr1);
  return 0;
}

1.因为strcpy函数要返回目标空间首元素的地址,所以先用ret来保存目标字符串dest的首元素地址

2.采取逐个赋值,当*src==‘\0’时进行完对应的赋值操作后(*dest++=*src++)这个表达式整体的值为’\0’,对应的ASCII码值即为0,所以跳出while循环,字符串拷贝操作结束

1.3. strcat

strcat:字符串追加函数

1.3.1 strcat函数剖析

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' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,如何?
这个问题等到我们模拟实现完strcat函数后再进行解释

1.3.2 strcat模拟实现

char* my_strcat(char* dest, const char* src)
{
  assert(dest != NULL);
  assert(src != NULL);
  char* ret = dest;
  while (*dest != '\0')
  {
    dest++;
  }
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}
int main()
{
  char arr1[30] = "hello ";
  char arr2[] = "world";
  my_strcat(arr1, arr2);
  printf("%s\n", arr1);//hello world
  return 0;
}

方法:

1.先找到dest即目标字符串的末尾位置(即’\0’的位置)

2.将src即源头字符串中的数据拷贝到目标字符串dest中

注意:该函数的返回值为目标字符串的首元素地址,所以需要用ret来保存dest字符串的首元素地址

相关文章
|
1月前
|
C语言 C++
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
26天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
22 0
|
1月前
|
存储 安全 编译器
深入C语言库:字符与字符串函数模拟实现
深入C语言库:字符与字符串函数模拟实现
|
1月前
|
C语言
C语言常见字符函数和字符串函数精讲
C语言常见字符函数和字符串函数精讲
|
1月前
|
C语言
【C语言】模拟实现深入了解:字符串函数
【C语言】模拟实现深入了解:字符串函数
|
3月前
|
安全 程序员 C语言
【C语言】字符串函数及其模拟实现
【C语言】字符串函数及其模拟实现
|
3月前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
63 0
|
3月前
|
存储 安全 编译器
【C语言篇】字符和字符串以及内存函数的详细介绍与模拟实现(上篇)
当然可以用scanf和printf输入输出,这里在之前【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)已经讲过了,这里就不再赘述,主要介绍只针对字符的函数.
55 0
|
4月前
|
存储 缓存 C语言
【C语言】字符函数,字符串函数,内存函数
C语言中的字符串函数和内存函数
57 0
【C语言】字符函数,字符串函数,内存函数
|
5月前
|
C语言
【c语言】字符串函数的模拟实现(二)
【c语言】字符串函数的模拟实现(二)
26 1