【C语言航路】第十一站:字符串、字符和内存函数(下)

简介: 【C语言航路】第十一站:字符串、字符和内存函数

(2)strerror的使用

#include<stdio.h>
#include<string.h>
int main()
{
  printf("%s\n", strerror(0));
  printf("%s\n", strerror(1));
  printf("%s\n", strerror(2));
  printf("%s\n", strerror(3));
  printf("%s\n", strerror(4));
  return 0;
}

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
  FILE* pf = fopen("test.txt", "r");
  if (pf == NULL)
  {
    printf("%s\n", strerror(errno));
    return 0;
  }
  fclose(pf);
  return 0;
}

(3)perror的使用

perror和strerror很相似,区别就是它是可以自己打印出错误信息的

perror在打印错误信息之前,会先打印出自定义的信息。

perror==printf+strerror

#include<stdio.h>
int main()
{
  FILE* pf = fopen("test.txt", "r");
  if (pf == NULL)
  {
    perror("fopen");
    return 0;
  }
  fclose(pf);
  return 0;
}

二、字符函数

对于这部分的函数,我们用的很少,当我们想要使用的时候,直接去文档搜索一下用法即可

1.字符分类函数

函数 如果它的参数符合下列条件则返回真
iscntrl 任何控制字符
isspace

空白字符:空格' ',换页'\f',换行'\n',回车'\r',制表符’\t‘,垂直制表符’\v‘

isdigit 十进制数0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 小写字母a~z,或者A~Z
isalnum 字母或数字,a~z,A~Z,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

2.字符转换函数

注意:这两个函数不会改变自身本来的值,是返回转换后的值

大写转小写 int tolow(int c)
小写转大写 int toupper(int c)

3.利用字符函数,将字符串中的大写字母改为小写打印出来

#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
  char arr[] = "I Have An Apple";
  int i = 0;
  int len = strlen(arr);
  for (i = 0; i < len; i++)
  {
    if (isupper(arr[i]))
    {
      arr[i] = tolower(arr[i]);
    }
    printf("%c", arr[i]);
  }
  return 0;
}

三、内存函数

我们已经有了字符串的操作函数,但是这些函数只针对字符串,我们有时候需要对整型数组等进行字符串类似的操作。所以我们就需要使用内存函数

1.memcpy

(1)memcpy的库函数文档

它有三个参数,第一个参数是目标空间的起始地址,第二个参数第源头空间的地址,第三个变量是拷贝num个字节

我们这个函数的功能是将source的前num个字节拷贝到destination中

返回一个指针指向dest处的指针,这个指针是void类型的

(2)memcpy的使用以及注意事项

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

这个函数在遇到 '\0' 的时候并不会停下来。

如果source和destination有任何的重叠,复制的结果都是未定义的。

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  memcpy(arr2, arr1, 20);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  memcpy(arr2, arr1 + 2, 20);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  memcpy(arr2, arr1 + 2, 17);
  return 0;
}

17个字节可行的原因是因为小端存储造成的

(3)模拟memcpy函数

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  assert(dest && src);
  void* ret = dest;
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret; 
}
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  my_memcpy(arr2, arr1 + 2, 20);
  return 0;
}

2.memmove

(1)memcpy的缺陷

在memcpy函数中,我们可以将一个地址的前n个字节复制到一个地址动。但是如果我们自身对自身进行拷贝的话,会出现一些问题

假如下图是src和dest的地址

那么按照我们的思路,最终的现象就是下图所示的

我们发现会出现1212这样似乎处于一个循环的情景,这与我们的字符串追加造成的现象是极其相似的。而且strcpy中也会出现这样的情况。为了避免出现这个现象,我们可以这样做,从后向前拷贝。

但是这样的话,如果dest又在src前面的话,又不符合我们的想法了,又会陷入一种类型于死循环的情况

这时候我们就需要从前向后拷贝了

于是我们发现,这个似乎需要分情况,经过我们的分析,我们可以得出以下结论

当然对于这四种情况,我们可以进行一次简化

如果是这样拷贝,那么我们的目标就实现了

而这个功能其实在memmove函数中就已经实现了

(2)memmove的库函数文档

这个函数的参数与memcpy是一样的,功能也是一样的,唯一不同的就是,可以自己拷贝自己了

(3)memmove的模拟实现

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
  assert(src && dest);
  void* ret = dest;
  if (dest < src)
  {
    //从前向后拷贝
    while (num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;
      src = (char*)src + 1;
    }
  }
  else
  {
    //从后向前拷贝
    while (num--)
    {
      *((char*)dest + num) = *((char*)src + num);
    }
  }
  return ret;
}
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  my_memmove(arr1, arr1 + 2, 20);
  return 0;
}

(4)memmove的使用以及注意事项

1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

2.如果源空间和目标空间出现重叠,就得使用memmove函数处理。

3.在vs上memcpy和memmove没有任何区别。但是其他编译器上不一定

3.memcmp

(1)memcmp的库函数文档

它的功能是比较ptr1和ptr2两个指针所指向空间的前num个字节

ptr1>ptr2返回大于0的数

ptr1==ptr2返回0

ptr1<ptr2返回小于0的数

(2)memcmp的使用

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3 };
  int arr2[] = { 1,2,5 };
  int ret = memcmp(arr1, arr2, 9);
  printf("%d", ret);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3 };
  int arr2[] = { 1,2,5 };
  int ret = memcmp(arr1, arr2, 8);
  printf("%d", ret);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,7 };
  int arr2[] = { 1,2,5 };
  int ret = memcmp(arr1, arr2, 12);
  printf("%d", ret);
  return 0;
}

4.memset

(1)memset的库函数文档

它是一个内存设置函数

将ptr空间中的前num个字节,以字节为单位设置为value

(2)memset的使用

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "hello world";
  memset(arr, 'x', 5);
  printf("%s\n", arr);
  memset(arr + 6, 'y', 5);
  printf("%s\n", arr);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  int arr[10] = { 0 };
  memset(arr, 1, 40);
  return 0;
}


总结

本节讲解了最常使用的字符串函数,内存函数,字符函数的使用,以及一些重要的函数的实现

如果对你有帮助,不要忘记点赞加收藏哦!!!

想获得更多优质内容,一定要关注我哦!!!

相关文章
|
9天前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
22 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
10天前
|
存储 算法 C语言
【C语言】字符常量详解
字符常量是C语言中处理字符数据的重要工具。通过单引号括起一个字符,我们可以方便地使用字符常量进行字符判断、字符运算和字符串处理等操作。理解字符常量的表示方法、使用场景和ASCII码对应关系,对于编写高效的C语言程序至关重要。
55 11
|
9天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
29 10
|
9天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
28 9
|
9天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
27 8
|
9天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
28 6
|
9天前
|
存储 C语言 开发者
【C语言】格式化输出占位符及其标志字符详解(基于ISO/IEC 9899:2024)
在C语言中,格式化输出通过 `printf` 函数等格式化输出函数来实现。格式说明符(占位符)定义了数据的输出方式,标准ISO/IEC 9899:2024(C23)对这些格式说明符进行了详细规定。本文将详细讲解格式说明符的组成部分,包括标志字符、宽度、精度、长度修饰符和类型字符,并适当增加表格说明。
28 6
|
9天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
61 6
|
9天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
36 6
|
9天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
19 5