【C语言进阶】字符串函数与内存函数的学习与模拟实现(上)

简介: 【C语言进阶】字符串函数与内存函数的学习与模拟实现(上)

1.字符串处理函数介绍

我们在C语言的从程序代码编写中,对字符和字符串的处理相当频繁,但是C语言本身并没有字符串类型。而字符串通常放在【常量字符串】或者【字符数组】中。其中,字符串常量适用于那些对它不做修改的字符串函数。

1.1 strlen 函数:

strlen 函数(string length)的作用是计算返回字符串中结束标识符 ’ \0 ’ 之前出现的的字符个数,因此,strlen函数所处理的字符串必须是以结束标识符 ’ \0 ’ 结尾的字符串。其·返回值类型为size_t 类型,该类型为无符号类型。

strlen 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
    //两中可行的初始化方式:
  char arr1[]="Hello!";
  char arr2[] = { 'H','e','l','l','o','!','\0' };
  int ret1 = strlen(arr1);
  int ret2 = strlen(arr2);
  printf("The length of arr1 is %d\n", ret1);
  printf("The length of arr2 is %d\n", ret2);
  return 0;
}

944ac19f322e4208867e77862f6501ca.png

我们使用字符型数组将字符串储存起来,接着使用 strlen 函数计算字符串 " Hello! " 中所有字符的数量使用双引号初始化字符串时,编译器将会自动在最后添加上结束标识符,并可以使用一个整型变量接收 strlen 函数的返回值,进行打印。

并且我们还说到,strlen 函数的返回类型为无符号数,因此 strlen 函数不可以直接用来比较两个字符串的大小。例如:

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

我们可能会认为计算出的结果为 3 - 6 为 -3,结果会执行 else 语句。但事实上,因为 strlen 函数的返回类型为无符号类型,得出的结果也是无符号类型,于是原本第一位即符号位上的数字也将被作为数据的一部分,因而实际得出的结果为一个非常大的整数,执行了 if 语句。

于是如果我们想要使用 strlen 函数来判断字符串的大小,可以通过让它的返回值由无符号数变为有符号数即可,即强制类型转换来实现:

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

1.2 strcpy 函数:

在之前的学习中,strcpy 函数也是我们经常使用的字符串处理函数之一。strcpy 函数(string copy)的作用是,可以将字符串从源地址复制至目的地址并且它会将源地址内的结束标识符 ’ \0 ’ 一并拷贝过去,因此源地址必须以 ’ \0 ’ 结尾,且目的地址也将以结束标识符结尾,通俗来讲就是用来实现字符串的复制和拷贝。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

strcpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[] = "Hellow!";
  const char arr2[10] = { 0 };
  printf("Before copy , the char inside arr1 are %s\n", arr1);
  printf("Before copy , the char inside arr2 are %s\n", arr2);
  printf("\n");
  strcpy(arr2, arr1);
    //strcpy函数使用格式为:strcpy(目的地址, 源地址)
  printf("After copy , the char inside arr1 are %s\n", arr1);
  printf("After copy , the char inside arr2 are %s\n", arr2);
  return 0;
}

2e0bb72ef709478eb43f383e25ce6a16.png

我们要特别注意的是 strcpy 函数返回的是目标空间的起始地址,该函数设置返回值值类型的目的是为了实现链式访问

1.3 strcat 函数:

strcat 函数(string catenate)的作用是,将源地址的字符串追加补充至目的地址处。与字符串拷贝函数相同,它在进行补充追加时是从目的地址的结束标识符处 ’ \0 ’ 开始追加的追加至源地址的结束标识符处停止。且它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

strcat 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[20] = "Hello!";
  const char arr2[20] = "Welcome!";
  printf("Before catenate , the char inside arr1 are %s\n", arr1);
  printf("Before catenate , the char inside arr2 are %s\n", arr2);
  printf("\n");
  strcat(arr2, arr1);
  printf("After catenate , the char inside arr1 are %s\n", arr1);
  printf("After catenate , the char inside arr2 are %s\n", arr2);
  return 0;
}

48b34d6cb9d44004bce8920ea6eec27c.png注意 strcat 函数无法追加自己。原因很好理解,我们在定义时就已经固定了字符数组的储存空间了,当追加自己时,相当于将自己与和与自己相同大小的字符数组,即两倍自身大小的数据放入自己的储存空间中,可想而知一定是不可行的。

1.4 strcmp 函数:

strcmp 函数(string compare)的作用为按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到结束标识符 ’ \0 ’ 或对应位置的字符不同若比较至结束标识符都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。

我们来看 strcmp 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[] = "abcd";
  const char arr2[] = "abz";
  int ret = strcmp(arr1, arr2);
  //若arr1大于arr2,则返回大于零的数
  //若arr1等于arr2,则返回等于零的数
  //若arr1小于arr2,则返回小于零的数
  if (ret > 0)
  {
    printf("arr1 > arr2\n");
  }
  else if (ret = 0)
  {
    printf("arr1 = arr2\n");
  }
  else
  {
    printf("arr1 < arr2\n");
  }
  return 0;
}

c1388d2932e54168ab913cf113740b92.png定义并初始化两个字符数组,接着对两个数组进行比较,根据 strcmp 函数的比较结果得到返回值,再根据返回值反馈我们想要的字符串比较结果。

注意并不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

1.5 strncpy 函数:

strncpy函数(string number copy)的作用为将指定长度的字符串复制到字符数组中,即表示把源地址中字符串开始的前n个字符拷贝到目的地址中。与 strcpy 相同,它同样会将源地址内的结束标识符 ’ \0 ’ 一并拷贝过去,因此源地址必须以 ’ \0 ’ 结尾,且目的地址也将以结束标识符结尾。并且,因为其作用为拷贝字符串,因此目标地址内的空间必须足够大,要有足够的空间容纳下源地址内的字符串,同时目的地址的空间必须是可变、可修改的。

strncpy 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
 //strncpy函数的使用:
int main()
{
  const char arr1[] = "Hellow!Welcome!";
  const char arr2[10] = { 0 };
  printf("Before copy , the char inside arr1 are %s\n", arr1);
  printf("Before copy , the char inside arr2 are %s\n", arr2);
  strncpy(arr2, arr1, 7);
  printf("After catenate , the char inside arr1 are %s\n", arr1);
  printf("After catenate , the char inside arr2 are %s\n", arr2);
 return 0;
}

e0df74ce132141d5900688cae1794f90.png我们看到,使用该函数,我们成功的将字符数组 arr1 中的前七个字符拷贝到了字符数组 arr2 中。

注意:若源字符串的长度小于我们传递过去的参数,则拷贝完源字符串之后,将会在目标后追加字符 ’ 0 ',直到拷贝至参数规定个数。

1.6 strncat 函数:

strncat 函数(string num catenate)的作用为从源地址处将指定长度的字符串追加补充到目的地址中与 strcat 函数类似,它在进行补充追加时也是从目的地址的结束标识符处 ’ \0 ’ 开始追加的,不同的是追加至参数限制的字符数处停止。但它同样要求目标地址内的空间必须足够大,要有足够的空间容纳下出家补充的字符串 ,同时目的地址的空间必须是可变、可修改的。

strncat 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[] = "Hellow!Welcome!";
  const char arr2[20] = "Welcome!";
  printf("Before copy , the char inside arr1 are %s\n", arr1);
  printf("Before copy , the char inside arr2 are %s\n", arr2);
  strncat(arr2, arr1, 7);
  printf("After catenate , the char inside arr1 are %s\n", arr1);
  printf("After catenate , the char inside arr2 are %s\n", arr2);
  return 0;
}

d753b58f68394c63bc72388a60f8832e.png我们可以看到,首先定义并初始化两个字符数组,接着打印追加补充之前它们各自空间内的内容进行确认,然后我们使用了 strncat 函数有限制的从数组 arr1 向 arr2 中追加补充了七个字符。

通过限制长度我们就可以实现字符数组对自己的追加补充了。

1.7 strncmp 函数:

strncmp 函数(string number compare)的作用为有限制的按照顺序依次比较两字符串对应位置字符的 ASCII 码值(注意不是比较两字符串的长度),直到参数限制位数位置上全部比较结束或对应位置的字符不同。若参数限制位数位置上的字符都比较结束且都没有不同则两字符串相等,若两字符串对应位置字符有不同,对应位置上字符 ASCII 码值小的字符串小于另一个字符串。

strncmp 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[] = "abcd";
  const char arr2[] = "abz";
  int ret = strncmp(arr1, arr2, 3);
    //比较前 3 个字符
  //若arr1大于arr2,则返回大于零的数
  //若arr1等于arr2,则返回等于零的数
  //若arr1小于arr2,则返回小于零的数
 if (ret > 0)
  {
    printf("arr1 > arr2\n");
  }
  else if (ret = 0)
  {
    printf("arr1 = arr2\n");
  }
  else
  {
    printf("arr1 < arr2\n");
  }
 return 0;
}

5575635cb28a4ea9b4e75fe90323b570.png其作用原理与作用过程,与 strncmp 函数十分类似,唯一不同便是限制了字符比较的位数

注意该函数的作用也不是比较字符串的长短,而是比较对应位置字符 ASCII 码值的大小

1.8 strstr 函数:

strstr 函数(string string)的作用为从一个字符串中寻找其字串,通俗来讲就是从一个字符串中寻找另一个字符串若找到目标字串则返回指向目标字串的指针,若没有找到目标字串则返回空指针(不返回)

strstr 函数的基本使用方式:

#include<stdio.h>
#include<string.h>
int main()
{
  const char arr1[] = "abcdefg";
  const char arr2[] = "cde";
 char* ret = strstr(arr1, arr2);
  //从ar1中寻找arr2
  if (ret == NULL)
  {
    printf("找不到该字符串!\n");
  }
  else
  {
    printf("成功找到该字符串'%s'!\n", ret);
  }
 return 0;
}

819552777e494a11ba9ddfa13cf73689.png我们定义并初始化了两个字符数组,并使用 strstr 函数进行字串寻找处理,并使用字符型指针接收汉函数的返回值,最后,对接收了返回值的指针 ret 进行判断,若为空则确定为没有从数组 arr1 中找到字串 arr2,否则便通过返回值,使用指针对字串地址内的数据进行访问。

该函数数有返回值,支持进行函数的链式访问


相关文章
|
5天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6
|
19天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
22天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
19 0
|
22天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
18 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
365 0
|
21天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
42 1
|
25天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
30天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
39 4
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
52 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配