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

简介: 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,今天的内存系列函数内容就到此结束啦~,如果后续想了解更多,就请关注我吧。

相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3
|
29天前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
1月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
159 1
|
1月前
|
程序员 C语言
C语言内存函数精讲
C语言内存函数精讲
|
24天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
20 0
|
1月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
|
1月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
52 0
|
1月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
40 0
|
1月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
66 0