内存系列函数+字符分类函数+字符转换函数(保姆级教程)

简介: C语言学习第二十一弹——教你学会内存系列函数和字符分类转换函数简化部分例题

前言


大步走在人生的道路上,沿途风光月霁,荆棘也开成了蔷薇

本章我们一起来学习内存系列函数+字符分类函数+字符转换函数

一、内存拷贝 — memcpy


1.memcpy函数的了解


void* — 通用类型的指针,可以接受任意类型数据的地址


注意:

拷贝的时候拷贝的是字节所以如果拷贝int类型中一个数字需要拷贝4个字节才等于一个整形

2.memcpy函数模拟实现


因为我们把源数据拷贝到目标空间希望看见目标空间里的数据然后把目标空间的地址返回去,所以返回数据写成void*

memcpy函数返回的是目标空间的起始地址

void — 通用类型的指针,可以接受任意类型数据的地址*

但是这种指针不能直接解引用和++ --运算

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
    //如果转换成整形类型,拷贝的时候拷贝7个字节解引用一次四个字节++跳过四个字节再解引用一次四个字节,访问了八个字节所以会出问题所以我们使用char*指针
    //但是这种强制类型转换只是临时的dest src下面进行时还是void*的,所以我们写成下面形式
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[8] = { 0 };
  my_memcpy(arr2, arr1, 20);
  return 0;
}

3.思考:


答案:

1 2 1 2 1 2 1 8 9 10

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memcpy(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);//打印的arr1所以是1212
  }
  return 0;
}

1 2 3 4 5 6 7 8 9 10

源头数据:1 2 3 4 5(绿色圈里)

目标数据:3 4 5 6 7(红色圈里)

当把源头数据拷贝到目标数据时目标数据3变成1,4变成2,再拷贝3的时候源头数据的3已经变成了1所以就把1再次拷贝到进去覆盖5同理2覆盖61覆盖7所以最终打印arr1出来是1 2 1 2 1 2 1 8 9 10;

所以我们发现:在内存重叠的时候,使用memcpy可能出现意想不到的效果~

建议:在内存重叠的情况,使用memmove函数

二、内存拷贝 — memmove


1.memmove函数的了解


由下面代码结果我们可以看出上面内存拷贝的问题用memmove得到了实现

2.memmove函数模拟实现


在拷贝的时候,我们发现1拷贝到3的位置2拷贝到4的位置;但是当把3拷贝到6的位置时3这个数据已经被1覆盖4的位置被数据2覆盖最终导致打印出来是1 2 1 2 1 2 1 8 9 10;那怎么办呢?

我们是不是可以倒着拷贝呢?我们将5拷贝到7的位置,4拷贝到6的位置,3拷贝到5的位置,2拷贝到4的位置,1拷贝到3的位置这样数据就不会被覆盖打印出就变成了1 2 1 2 3 4 5 8 9 10;

所以我们遇到这种情况就按倒着拷贝吗

那我们再来看看下面这幅图,当倒着拷贝的时候7拷贝到5,6拷贝到4,当把5拷贝到3的位置时5已经被7覆盖所以显然不行,最终会得到7 6 7 6 7 6 7 8 9 10

那我们正着拷贝的时候,3拷贝到1,4拷贝到2,5拷贝到3,6拷贝到4,7拷贝到5显然是可以的,没有发生数据覆盖情况

所以我们该怎么做呢

(1).结论一:


我们知道这是一个数组左边是低地址,右边是高地址,当dest小于src且与src重叠时,我们先拷贝源头的最左边的数字也就是先拷贝3也就是正着拷贝(从前向后);

(2).结论二:


当dest大于src且与src重叠时,我们先拷贝源头的最右边的数字也就是先拷贝7到9也就是倒着拷贝(从后向前);

(3).结论三:


当dest大于或小于src且与src没有重叠时,我们先拷贝就不需考虑从哪边开始拷贝且从哪边都可以拷贝;

(4).最终的模拟实现:


C语言:memcpy拷贝不重叠的内存;重叠的就交给memmove

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

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

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
  char* ret = dest;
  assert(src && 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[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr1, arr1+2, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

三、内存比较 — memcmp


memcmp函数的了解


比较从ptr1和ptr2指针开始的num个字节


我们通过代码来例举:

当比较16个字节也就是4个整形也就是前四位数时,它们是相同的,无可置疑应该返回0,结果也确实是0;

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

那当我们把16个字节改成17个字节呢还是0吗?

答案是-1;这是为什么呢?


01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00

01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00

存在小端存储当比较字节数为17时,刚好比较到了05和06所以arr1<arr2返回<0的数,但是当你把5改成7时就返回了>0的数

四、内存设置 — memset


memset函数的了解


注意:

以字节为单位设置的

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

五、字符分类函数


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 任何可打印字符,包括图形字符和空白字符

六、字符转换函数


int tolower ( int c );

int toupper ( int c );

#include<stdio.h>
int main()
{
  printf("%c\n", toupper('a'));//小写转换成大写
  printf("%c\n", tolower('A'));//大写转换成小写
  return 0;
}

例题:

输入的字符全部转换成小写

#include<stdio.h>
#include<ctype.h>
int main()
{
  char arr[128] = { 0 };
  gets(arr);
  int i = 0;
  while (arr[i])
  {
    if (isupper(arr[i]))
    {
      arr[i] = tolower(arr[i]);
    }
    printf("%c", arr[i]);
    i++;
  }
  return 0;
}

总结


Ending,今天的内存系列函数内容就到此结束啦~,如果后续想了解更多,就请关注我吧。

相关文章
|
13天前
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
13 2
|
28天前
|
存储 C语言
C语言学习记录——动态内存函数介绍(malloc、free、calloc、realloc)
C语言学习记录——动态内存函数介绍(malloc、free、calloc、realloc)
24 1
|
10天前
|
C语言
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
19 0
|
14天前
|
C语言
字符串和内存函数(1)
字符串和内存函数(1)
25 7
|
10天前
|
C语言
【C语言】:4大内存函数
【C语言】:4大内存函数
13 2
|
14天前
字符串和内存函数(2)
字符串和内存函数(2)
23 5
|
20天前
|
安全 C语言
【C语言基础】:内存操作函数
【C语言基础】:内存操作函数
|
23天前
|
运维 Serverless Nacos
Serverless 应用引擎产品使用合集之在访问量过大的情况下,函数配置的cpu和内存会自动扩容吗
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
28天前
|
C语言 C++
C语言学习记录——内存函数(memcpy、memmove、memcmp、memset、模拟实现memcpy、模拟实现memmove)
C语言学习记录——内存函数(memcpy、memmove、memcmp、memset、模拟实现memcpy、模拟实现memmove)
22 3
|
6天前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
13 0