【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,否则便通过返回值,使用指针对字串地址内的数据进行访问。

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


相关文章
|
1月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
63 16
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
36 3
|
1月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
19 2
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
51 1
|
3月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
635 1
|
2月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
3月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
3月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
38 3
|
3月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
73 1
|
3月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。

热门文章

最新文章