【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;
}


总结

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

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

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

相关文章
|
6天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
12天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
35 7
|
9天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
12 0
|
9天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
11 0
|
6月前
|
存储 编译器 C语言
在C语言中的数组和字符串
在C语言中的数组和字符串
|
2月前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
4月前
|
安全 C语言
C语言8 数组与字符串
C语言8 数组与字符串
28 0
|
存储 机器学习/深度学习 Linux
【C语言】语言篇——数组和字符串
【C语言】语言篇——数组和字符串
49 0
|
存储 C语言
C语言之指针(指针数组以及指针的指针和字符串)
C语言之指针(指针数组以及指针的指针和字符串)
93 0
|
存储 C语言
【C语言程序设计】知识点汇总3——数组、字符串数组、字符串输入输出相关注意事项
【C语言程序设计】知识点汇总3——数组、字符串数组、字符串输入输出相关注意事项
260 0
【C语言程序设计】知识点汇总3——数组、字符串数组、字符串输入输出相关注意事项