C语言进阶第六课-----------字符分类函数和内存的开辟 2

简介: C语言进阶第六课-----------字符分类函数和内存的开辟

字符转换

前面我们只是理解了判断字符,如果要转换字符也有对应的库函数

toupper

小写字母是以下任意字母:a b c d e f g h i j k l m n o p q r s t u v w x y z,分别翻译为:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z。

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int main()
{
  int i = 97;
  for (; i <= 122; i++)
  {
    printf("%c\n", toupper(i));
  }
  return 0;
}

如果是小写就转换,不是就不转换

tolower

同理这个函数就是把大写转换成小写

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int main()
{
  int i = 65;
  for (; i <= 90; i++)
  {
    printf("%c\n", tolower(i));
  }
  printf("%c\n", tolower('a'));
  return 0;
}

如果是大写就转换,不是就不转换

内存相关函数

memcpy

前面我们已经学习过了strcpy,这个函数只适合于字符串拷贝,如果要拷贝整数数组,浮点型数组就会没办法,但是memcpy就可以解决

这个函数有三个参数,一个是目标空间,一个是起始地址,以及要拷贝的字节总大小

如果看过我模拟出来的qsrot函数就会明白,这里为啥要拷贝字节总大小,我们只把对应的字节大小进行拷贝过去

include<stdio.h>
int main()
{
  int arr[20] = { 1,2,3,4,5,6,7,8,9 };
  int arr1[10] = { 0 };
  memcpy(arr1, arr, sizeof(int) * 9);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

如果我们要模拟实现出来

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<assert.h>
void* my_memcpy(void* destintion, const void* soured, size_t num)
{
  //不为空
  assert(destintion && soured);
  //一个一个字节进行拷贝,要使用char*, 加1访问一个字节
   const char* su = soured;
   char* de = destintion;
   while (num)
   {
     *de++ = *su++;
     num--;
   }
   return destintion;
}
int main()
{
  char arr[20] = "abcd\0f";
  char arr1[10] = { 0 };
  my_memcpy(arr1, arr, sizeof(arr[0]) * 10);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%c ", arr1[i]);
  }
  return 0;
}

如果当我们使用这个模拟函数进行自身部分的拷贝就会发现,和库函数memcpy有差异

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
  int arr[20] = {1,2,3,4,5,6,7,8,9};
  memcpy( arr + 2, arr, sizeof(arr[0]) * 5);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

库函数的结果

我们模拟函数的结果:

可以看出两者的差异

看到这里就会有疑问,是不是模拟错了?

其实标准值规定,memcpy来实现不重叠的内存拷贝,而重叠的内存拷贝一般不使用memcpy而是使用memmove,就好比我们考试只需考60分,90分就要其他人去考吧,而我们却发现memcpy连重叠的拷贝也能做到,这就是超预期了,而我们模拟出来的函数只是具备了拷贝不重叠的内存拷贝功能,vs的库函数memcpy两者具备,但是在一些编译器里就会不一定两种功能都有。

memove

从这里可以看出memmove函数的参数和memcpy是一样的,所以memove函数具备了memcpy的所有功能

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int arr1[10] = { 0 };
  memmove(arr1, arr, sizeof(arr[0]) * 9);
  return 0;
}

我们来模拟一下

如果模拟这个函数很容易就会把memcpy的功能加上重叠的功能,重叠的方法很多人就会想到我要重新开辟一块临时空间存放和arr一样的,然后进行一一的拷贝,然后再覆盖原来的arr,这个方法是可行的,但是这样很麻烦,那有没有在一个数组内就可以进行拷贝呢?答案是有的

如上图

如果我们要3-7拷贝到1-5的位置上去,我们可以先把3拷贝到1然后依次往下,就可以解决原来数字被覆盖的问题,

如果我们要把1-5拷贝到5-9的位置我们就先拷贝5到9的位置上去,往后依次往前

#include<stdio.h>
void* my_memmove(void* arr1, const void* arr, size_t num)
{
  char* pa1 = arr1;
  const char* pa = arr;
  // 后往前拷贝
  if (pa1 > pa)
  {
    pa1 += num - 1;
    pa += num - 1;
    while (num)
    {
      *pa1 = *pa;
      pa1--;
      pa--;
      num--;
    }
  }
  else
  {
    //前往后拷贝
    while (num)
    {
      *pa1 = *pa;
      pa1++;
      pa++;
      num--;
    }
  }
  return arr1;
}
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int arr1[10] = { 0 };
  my_memmove(arr, arr + 2, sizeof(arr[0]) * 5);
  return 0;
}

这里我们要注意一下数组arr的第n个元素的地址是arr+(n - 1)

memset

填充内存块

把从ptr开始的num个字节填充为value

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "abcdefg";
  memset(arr, 't', 5);
  printf("%s", arr);
  return 0;
}

结果为:

所以我们可以认为memset是以字节为单位的进行设置的,如果不相信,下面为例

#include<stdio.h>
#include<string.h>
int main()
{
  int arr[10] = { 0 };
  memset(arr, 1, 40);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

结果为:

这里就是arr里面 存储的内存,我们的想法是把arr每个元素设置成1,而结果却不对,memset是以字节为单位进行设置的,需要注意一下,在vs是小端存储(高阶数存放在低地址) 结果为01010101

如果我们要模拟实现就是

#include<stdio.h>
#include<string.h>
void* my_memset(void* ptr, int value, size_t num)
{
  char* pr = ptr;
  while (num)
  {
    *pr = value;
    pr++;
    num--;
  }
  return ptr;
}
int main()
{
  char arr[] = "abcdefghij";
  int arr1[5] = { 0 };
  my_memset(arr, '0', sizeof(arr[0]) * 5);
  return 0;
}

memcmp

比较两个内存块的内容

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9 };
  // 数组的存储方式:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00
  int arr[] = { 1,2,3,6 };
  // 数组的存储方式:01 00 00 00 02 00 00 00 03 00 00 00 06 00 00 00
  printf("%d", memcmp(arr, arr1, 16));
  return 0;
}

简单的理解,和strcmp的比较方式是一样的,一旦匹配到不相等的内存就会返回

如果不相信还有一个例子

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9 };
  // 数组的存储方式:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00
  int arr[] = { 1,2,3,0x11223301};
  // 数组的存储方式:01 00 00 00 02 00 00 00 03 00 00 00 01 33 22 11
  printf("%d", memcmp(arr, arr1, 16));
  return 0;
}

我们来模拟一下

#include<stdio.h>
#include<string.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
  while (num)
  {
    if (*((char*)ptr1) == *((char*)ptr2))
    {
      ptr1 = (char*)ptr1 + 1;
      ptr2 = (char*)ptr2 + 1;
    }
    else
    {
      return *((char*)ptr1) - *((char*)ptr2);
    }
  }
  return 0;
}
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int arr1[] = { 1,2,3,4,5,0x11223306 };
  int num = my_memcmp(arr1, arr, 6);
  printf("%d", num);
  return 0;
}

总结

到这里字符串相关的函数就学习完了,有不懂的可以私聊我

相关文章
|
6天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6
|
19天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
25天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
53 7
|
22天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
19 0
|
22天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
18 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
366 0
|
21天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
44 1
|
26天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
30天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
41 4