【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语言库函数 — 内存函数(含模拟实现内存函数)
15 0
|
17天前
|
编译器 C语言 C++
【C语言】memset()函数(内存块初始化函数)
【C语言】memset()函数(内存块初始化函数)
23 0
|
17天前
|
编译器 C语言 C++
【C语言】memcpy()函数(内存块拷贝函数)
【C语言】memcpy()函数(内存块拷贝函数)
37 0
|
8天前
|
存储 编译器 C语言
深入探索C语言动态内存分配:释放你的程序潜力
深入探索C语言动态内存分配:释放你的程序潜力
26 0
|
12天前
|
存储 C语言
【我爱C语言】详解字符函数isdigit和字符串转换函数(atoi和snprintf实现互相转换字符串)&&三种strlen模拟实现1
【我爱C语言】详解字符函数isdigit和字符串转换函数(atoi和snprintf实现互相转换字符串)&&三种strlen模拟实现
|
13天前
|
C语言
【C语言】第三回 关于字符串,语句和注释的使用2
【C语言】第三回 关于字符串,语句和注释的使用
|
17天前
|
编译器 C语言 C++
【C语言】calloc()函数详解(动态内存开辟函数)
【C语言】calloc()函数详解(动态内存开辟函数)
23 0
|
17天前
|
存储 编译器 程序员
【C语言】内存的动态分配与释放
【C语言】内存的动态分配与释放
26 0
|
18天前
|
存储 编译器 程序员
【C语言】整形数据和浮点型数据在内存中的存储
【C语言】整形数据和浮点型数据在内存中的存储
12 0