【C语言】字符串函数+内存操作函数

简介: 【C语言】字符串函数+内存操作函数

前言:这篇文章内容含量较大,可以分为两次观看(有实力也可以一次读完😎)

97c35a011b1a49b6a239c2b8771da1f8.jpeg

一、库函数介绍

1.1strlen函数(字符串长度函数)

Function of a function is Get the length of a string.


1b755876a98d4d8fac20d9581841a6d0.png


1.返回类型是size_t,通过转到定义可以发现size_t是一个无符号整型,下面就是转到定义后的结果typedef unsigned __int64 size_t;


2.参数是const char*,规定了传过来的字符串地址是const修饰,也就说明字符串不允许被修改


3.字符串本身是以’\0’作为结束标志,strlen函数返回的是字符串中’\0’之前的字符个数


1.2 strcpy函数(字符串拷贝函数)


Copies the C string pointed by source into the array pointed by destination, including the

terminating null character (and stopping at that point)

将源头指向的C字符串赋值到目的指针指向的数组中,包括终止空字符(并且在该位置停止)56094d0007cc4eb98c1c5d2ad6ac692e.png


56094d0007cc4eb98c1c5d2ad6ac692e.png

1.返回类型是目的地字符串的地址char*,参数分别是不可改变的指向源头字符串的指针和可以改变的指向目的地字符串的指针


2.源头字符串必须要以\0结束,拷贝时会将\0也拷贝过去,这样返回的目的地地址我们用于打印时,也有个结束标志嘛


3.目标空间必须足够大,能够存放的下源头字符串


1.3 strcat函数(字符串追加函数)


Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is includedat the end of the new string formed by the concatenation of both in destination.

将源字符串追加到目标字符串后面。目标字符串末尾的空字符会被源字符串的首字符覆盖掉,并且空字符会被包含在新连接成的字符串末尾后面87431767092a49b0b9bc0d6112e37fe3.png


87431767092a49b0b9bc0d6112e37fe3.png

1.返回类型为目标字符串的首地址,两个参数分别为不可改变的源字符串首地址和可以改变的目的地字符串地址


2.这里要注意,strcat函数是不可以用自己追加自己的,我们这一部分只讲函数的介绍,不能自己追加自己的原因,我们到库函数的模拟实现那一部分,再去介绍


3.源字符串必须以\0结束,这样我们再将源字符串追加到目标字符串时,才会有一个结束条件


4.而且,目标空间必须足够大,能存放追加后的新字符串


1.4 strcmp函数(字符串比较函数)


This function starts comparing the first character of each string. If they are equal to each

other, it continues with the following pairs until the characters differ or until a terminating

null-character is reached

这个函数开始比较每个字符串的第一个字符,如果他们是相等的,则继续比较字符串的下一个字符,直到这对字符不相等,或者遇到空字符,它的比较才会停止


0f32f922f3754c4e812ba8a9f37f0e2b.png0ae930c0513345c284852a02ba959f5f.png

1.这个函数的返回类型是一个int型的数据,由第二张图片可知,当字符串1比较结果小于字符串2时返回值是一个小于0的数字(vs环境下的数字是-1),等于时返回0,大于时返回一个大于0的数字(vs环境下的数字是1)。这个函数的参数类型是两个指向不可改变的字符串的指针,左小于右返回小于0的数字,左大于右返回大于0的数字,(我怕你们在比较时,分不清哪个比哪个大的结果是什么,所以你就按照一般习惯,从左向右比较,这样比较好记)


1.5 strncpy函数(有拷贝字符个数限制的字符串拷贝函数)


Copies the first num characters of source to destination. If the end of the source C string(which is signaled by a null-character) is found before num characters have been copied,destination is padded with zeros until a total of num characters have been written to it.

复制源头字符串的第一个字符到目的地字符串中。如果目的传num个字符,在传之前遇到了源头字符串的空字符\0,但此时传过去的字符数是小于num的,则用\0来填充到目的地字符串中,直到传够num个字符为止

01f48d01474844eca4a8d7d865d89ae2.png


1.6 strncat函数(有追加字符个数限制的字符串追加函数)


Appends the first num characters of source to destination, plus a terminating null-character.If the length of the C string in source is less than num, only the content up to the terminatingnull-character is copied

追加源字符串到目的地字符串后面,在追加后的字符串末尾加上一个\0。如果source中的C字符串长度小于num,则只追加终止符null之前的内容,这个函数并不会像strcpy一样去补齐空字符直到达到num个数,它追加过程中若遇到空字符,则停止追加

a32b1b21c8f34624af9eba1085930e4c.png

1.这里我们给大家看一眼strncat的使用方式,追加6个字符or not(注意空格也是一个空字符,所以总共是6个字节)

0e26cb181e5d4c6f8e606903dfc401c5.png



1.7 strncmp函数(PLUS版strcmp)


函数功能与strcmp函数十分相似,唯一你手动操控的就是,你规定两个字符串需要比较的字符个数

用一段代码来看一下函数的使用

int main()
{
  const char*p1 = "abcdzf";
  const char* p2 = "abcqwer";
  /*int ret=strcmp(p1, p2);*/
  int ret = strncmp(p1, p2, 5);
  //d>q,只要你传大于等于4的数字,返回结果都是-1,也就是小于0的数字
  printf("ret=%d\n", ret);
  return 0;
}


1.8 strstr函数(查找子串函数)


9a2dfd53eb144611ab18d9707b3464a7.png


Return Value:


Each of these functions returns a pointer to the first occurrence of strCharSet in string, or NULL if strCharSet does not appear in string. If strCharSet points to a string of zero length, the function returns string

1.这其实就是个字符串查找函数,如果在string中存在strcharset,则返回string中首次出现strcharset的首地址, 如果strCharSet没有出现在string中则返回NULL。


特殊情况,如果strcharset指向一个长度为0的字符串,则函数返回字符串string


1.9 strtok函数(切割有标记符的字符串函数)

char * strtok ( char * str, const char * sep );


1.第一个参数是要被切割的字符串,第二个字符串是被切割的字符串中所包含的分割符的集合


2.strtok函数找到str中的下一个标记,并将其用\0结尾,然后返回一个指向这个标记之前的字符串的首字符地址的指针,然后继续向字符串后面去找还有没有分隔符,如果有,则继续重复前面的操作

这里要注意一点,strtok函数是会改变被操作的字符串,所以在使用strtok函数切割字符串时,一般切割的都是这个字符串的临时拷贝的内容。


3.当strtok函数的第一个参数不为NULL时,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置


4.当strtok函数的第一个参数为NULL时,函数将在同一个字符串中被保存的位置开始,查找下一个标记,并且再把他变成’\0‘,并且继续保存这个位置,以便下一次这个函数继续从这个位置向后找标记(当然继续向后找时,我们还是要给strtok函数的第一个参数传一个NULL)


5.所以这个函数的使用方式就是,我们第一次去传要切割的字符串的首字符地址,如果一次没有切割完成的话,下一次我们就传NULL空指针,strtok函数会自动找到上一次我们保存的分隔符地址,往后找下一个分隔符


6.如果字符串中已经不存在标记了,则返回空指针,结束对于字符串的切割


1.10 strerror函数(打印错误信息函数)

char * strerror ( int errnum );


cc1b9f77c19f4513b69cb12af936182c.png


1.11 memcpy函数(PLUS版strncpy)


1c2688bc7b6d49a9932c9edfbb7c07e5.png

Remarks:


The memcpy function copies count bytes of src to dest. If the source and destination overlap, this function does not ensure that the original source bytes in the overlapping region are copied before being overwritten. Use memmove to handle overlapping regions.

这个函数会将src中count个字节的数据拷贝到dest中。如果源地址和目的地址发生重叠,则复制重叠区域中某些数据将会被覆盖。应该用memmove来处理重叠区域


1.这种函数可以操作任意类型数据,整型,浮点型,结构体类型都可以,功能更加健壮(相比只能拷贝字符串的strcpy函数)


2.当拷贝的内存出现重叠时,拷贝的结果都是未定义的,什么叫内存重叠呢?


比如我们把一个数组内容1 2 3 4 5 6 7 8 9 10中12345拷贝到34567的位置memcpy的结果是不确定的


3.函数会以源头数据为开始向后复制num个字节到目的地位置(这个就很像plus版的strncpy函数,memcpy就是能操作不同类型数据的拷贝了)


1.12 memmove函数(PLUS版memcpy)

void * memmove ( void* destination, const void * source, size_t num );


1.这个函数也没什么可介绍的了,他与memcpy唯一不同的就是,他可以处理源内存块儿和目标内存块儿重叠的情况,函数功能更加健壮

2.所以在拷贝数据发生内存重叠的情况时,我们要使用memmove函数


1.13 memcmp函数(PLUS版strncmp)

int memcmp ( const void * ptr1, const void * ptr2, size_t num );


这个函数也没什么可介绍的了,他与strncmp函数相比就是可以操作不同类型的数据

1.14 memset函数(内存设置函数)


memset函数:
Sets buffers to a specified character.
void *memset( void *dest, int c, size_t count );


1.dest指的是要改变的变量的地址

2.int就是我们要改变成什么形式,可以是字符(ascll码值),也可以是整数

3.count就是要改变的字节数

这里要注意一点,memset函数改变变量内容时,是一个字节一个字节去改变的

int main()
{
  char arr1[10] = { 0 };
  memset(arr1, '#', 9);//注意你这里改变了9个字节的大小
  int arr2[10] = { 0 };
  memset(arr2, 1, 10);//注意你这里改的是数组元素共40个字节中的前10个字节
  printf("%s\n", arr1);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr2[i]);
  }
  return 0;
}
切记memset函数是一个字节一个字节的改动内容的


1.15 字符转换函数和字符分类函数

bfa2aa1fc540493c8480870c270ee084.png


1.字符分类函数用于测试字符是否属于特定的字符类别,如字母字符、控制字符等等。

2.字符转换函数用于改变字符的大小写,我们可以改变字符串中的大小写,也可以改变单个字符的大小写

ba841eb92cc04358a72ecede4063684b.png



二、库函数的具体介绍(代码调试的形式)以及模拟实现部分库函数

1.strlen函数模拟实现

1.1计数器的方法

int my_strlen(char* ps)
{
  int count = 0;//创建一个计数器功能的变量
  while (*ps++)
  {
    count++;
  }
  return count;
}
int main()
{
  char arr[] = {"abcdef" };
  printf("%d", my_strlen(arr));
  return 0;
}


fb6bbc54637441d48c7492ad35c06e03.png


1.2递归的方法

int my_strlen(char* str)
{
  if (*str == '\0')
  {
    return 0;
  }
  else
  {
    return 1 + my_strlen(str + 1);
  }
}
int main()
{
  char *arr = "abcdef";
  printf("%d", my_strlen(arr));
  return 0;
}



1.3指针-指针的方法(有阅读难度的代码,动脑阅读)

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
  assert(str);
  char* end = str;
  while (*end++ != '\0');
  size_t n = (size_t)(end - str) - 1;
  return n;
}
int main()
{
  char arr[] = "abcde";
  size_t n = my_strlen(arr);
  printf("%u\n", n);
  return 0;
}


1.4有关strlen的一个题(加深理解strlen函数)

#include <stdio.h>
int main()
{
 const char*str1 = "abcdef";
 const char*str2 = "bbb";
 if(strlen(str2)-strlen(str1)>0)
 {
 printf("haha\n");
 } 
 else
 {
 printf("hehe\n");
 }
 return 0;
}


949455f1debd44fead70508cb4d5e849 (1).png

注意strlen的返回值是无符号整型,无符号整型之间的运算结果还是无符号整型,则运算结果永远大于0,程序永远输出haha


2. 模拟实现strcpy

char* my_strcpy(char* dest, const char* src)//库函数strcpy的返回类型是char*
{
  assert(dest && src);
  char* ret = dest;
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
  //返回目的空间的起始地址
}
int main()
{
  char arr1[] = "abcdefghi";
  char arr2[] = "bit";
  错误的代码形式:
  char arr2[]={'b','i','t'};
  //库函数strcpy会把\0也拷贝过去
  printf("%s\n", my_strcpy(arr1, arr2));
  return 0;
}


这里的无法追加自己的原因,给大家详细解释一下 (图片解释的非常详细,好好看图6cc05042c4bf4d89a7a1b233360f4024.png


4. 模拟实现strcmp函数

int my_strcmp(const char* str1, const char* str2)
{
  assert(str1 && str2);
  while (*str1 == *str2)//我们得考虑如果两个字符串字符相等的时候,我们该怎么做
  //其实也很简单,如果相等,我们将两个指向字符串的指针向后挪动即可
  {
    if (*str1 == '\0')
//这里其实改成*str2==\0也可以,因为只要你在while循环里面那你的两个字符串就是存在
//相等的情况,等到*str1或者*srt2有一个等于\0时,也就说明你查找完毕了,
//这时两个字符串是完全相等的
    {
      return 0;
    }
    str1++;
    str2++;
  }
  if (*str1 > *str2)
    return 1;
  else 
    return -1;
}
int main()
{
  const char* p1 = "abcdef";
  const char* p2 = "sqwer";
  int ret = my_strcmp(p1, p2);
  if (ret == 0)
    printf("p1=p2");
  else if (ret > 0)
    printf("p1>p2");
  else
    printf("p1<p2");
  return 0;
}


5. 模拟实现strstr函数

const char* my_strstr(const char* p1, const char* p2)
{
  assert(p1 && p2);
//这里将传过来的指针,先做一个临时拷贝,这样我们就可以对拷贝进行操作,以免指针受到改变,无法返回原来的样子
  const char* s1 = p1;
  const char* s2 = p2;
  const char* cur = p1;
  if (*p2=='\0')
  {
    return NULL;
  }
  while (*cur )
//从p1开始查找,所以外层while循环就是遍历p1字符串,开始查找其中是否拥有子串p2
  {
    s1 = cur;
    s2 = p2;//一次找不到,还得重新以下一个字符为起点重新进行判断
    while ((*s1!='\0')&&(*s2 != '\0') && (*s1  == *s2 ))
    {
      s1++;
      s2++;
    }
//跳出内层while循环的情况是,找子串找完了,或者没有找到相等的字符了,也就是*s1!=*s2了,其中*s1==\0时的这种
//情况也被包含到没有找到相等的字符里面了,所以我们只要在跳出循环外边分两种情况就可以了,找到子串和没有找到
//子串,让指向str1的cur指针向后移动一个字节,开始下一轮的查找,排查str1是否还含有str2子串(这里习惯将源头字符串
//和被查找的子串分别称为str1和str2)
    if (*s2 == '\0')
    {
      return cur;//找到字串,我们返回源字符串中存在子串起始字符的地址,所以打印的是源字符串的部分内容
    }
    cur++;
  }
  return NULL;//找不到子串
}
int main()
{
  const char* p1 = "abbbcdef";
  const char* p2 = "bbc";
  const char* ret=my_strstr(p1, p2);
  //如果有子字符串返回首字符地址,反之返回空指针
  if (ret == NULL)
    printf("字串不存在\n");
  else
    printf("%s\n", ret);
  return 0;
}


6852148755e64c5296c079083b355b41.png


6. 如何使用strtok函数(这里不做模拟实现,只要学会如何使用即可)

int main()
{
  char arr[] = "wang.ya.nan2022@outlook.com";
  const char* p = "@.";
  char buf[1024] = { 0 };
  strcpy(buf, arr);//将arr拷贝到buf里,切割buf的字符串即可,也就是切割拷贝内容
  char* ret = NULL;
  //我们需要将strtok函数的返回值存在一个char*的指针变量里面
  for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
  //for循环的三个参数分别是,初始化表达式,循环变量判定表达式,循环变量修正表达式
  //我们这里的循环变量就是接收strtok函数返回值的ret指针变量
  {
    printf("%s\n", ret);
  }
  1.for循环第一部分的代码只会被调用一次,很好解决了,我后续传空指针的重复步骤
  2.for循环执行完初始化的部分后,会一直循环第二部分和第三部分的步骤内容
  return 0;
}



7.对长度不受限制的strlen,strcpy,strcmp,strcat函数做一个小总结


这些函数他们在发生作用时,是不受长度限制的,对比strncpy,strncmp,strncat等函数,他们的参数也只有两部分(strlen不包括),源字符串和目的地字符串。而这些函数名加上n之后,其中参数也有了要操作的字节数,参数个数变为了3个


我们这里再来看一段代码,加深xdm对于这些长度不受限制的函数的记忆,下面来看一段代码

int main()
{
    char arr1[5] = "abcd";
  const char arr2[] = "hello bit";
  strcpy(arr1, arr2);
//为什么说以上三个函数是长度不受限制的字符串函数,虽然arr1数组的大小不够存放hello bit,但依然能
//在终端看到hello bit,虽然程序已经挂了,但不影响strcpy函数一定要把\0拷贝过去,只有拷贝完\0,
//这个函数才会停止
  printf("%s", arr1);
  return 0;
//所以strcpy函数是不安全的
}

d5b67923c1e74c7f92fd74b7c63126d0.png


这里我们可以看到,即使arr1数组无法存放hello bit这个字符串,我们的strcpy函数依然要将其拷贝过去,直到拷贝完\0结束,哪怕是程序已经挂掉了,也不停下来,继续他的拷贝。


从这一案例,我们可以了解到,strcpy函数是不安全的,他也是不受长度限制的


下面我们再来看一段代码,与strcpy函数对比一下

int main()
{
    char arr1[5] = "abcd";
  const char arr2[] = "hello bit";
  strncpy(arr1, arr2,9);
//为什么说以上三个函数是长度不受限制的字符串函数,虽然arr1数组的大小不够存放hello bit,但依然能
//在终端看到hello bit,虽然程序已经挂了,但不影响strcpy函数一定要把\0拷贝过去,只有拷贝完\0,
//这个函数才会停止
  printf("%s", arr1);
  return 0;
//所以strcpy函数是不安全的
}

76d55af2351940bebd26fda41be74690.png

而当我们使用strncpy函数时,这时我们的终端是无法输出正确的结果的,他会出现乱码,与strcpy函数相比,是比较安全的。所以加n之后,虽然这些函数变得有了长度的限制,但也变得更加安全了


8.strncpy函数的功能详解(前面的文字介绍不容易记住,这里用调试帮助大家记忆)

8.1当内容长度小于我们的个数限制时


3f11b3ac63794ae1bbfa7439bc2494e4.png


从调试后的两张图片我们可以看出,strncpy使用时,你让它拷贝多少个字节,它就拷贝多少,如果要拷贝的内容不够,我们就用\0来补齐剩余的字节数,直到达到你要求的个数限制


a597f87c91e346688a70d14ce0d015ac.png


8.2当内容长度溢出我们的个数限制时


263ed9ccf67442bf9671f8691a95052a.png

……………………图片分割线


b00cd351e89d4d4282789ecff499b7e6.png


通过上面的这两张图片我们可以看出,当内容溢出时,我们的strncpy函数是不会给你添加\0的,所以当printf函数打印时,无法找到\0就会打印出来乱码,出现了越界访问的程序错误

9.strncat函数的功能详解


9.1当内容长度小于我们的个数限制时

int main()
{
  char arr1[30] = "hello\0**************";
  char arr2[] = "bit";
  strncat(arr1, arr2, 6);
  //如果传的数字比字符串长,那就只把字符串传过去,再补个\0就完事了
  return 0;
}


449f8c7758ea4421b8b907f8503a2781.png


8562e35c07b6476b92f7afa9097a6d1a.png


哎呀,从调试中我们就可以完美的观察到,我们的strncat函数是只补一个\0的,不会像strncpy一样,内容小于个数时,疯狂添加\0

9.2当内容长度溢出我们的个数限制时

int main()
{
  char arr1[30] = "hello\0**************";
  char arr2[] = "bit";
  strncat(arr1, arr2, 2);
  //如果传的数字比字符串短,那就只把相应大小的字符串传过去,再补个\0就完事了
  return 0;
}


2e08daa7e0744e16a4682ad183afb862.png


……………………图片分割线


4c4f806d9c5547bab6cee40379286777.png


从调试的窗口图片中我们可以看出,当内容长度溢出我们的个数限制时,我们的strncat函数是会自动在后面补充一个\0的,这一点与strncpy也是不一样的


10.strncmp函数的功能详解(简单说一下,这个没什么好讲的😪😪😪)

int main()
{
  const char*p1 = "abcdzf";
  const char* p2 = "abcqwer";
  /*int ret=strcmp(p1, p2);*/
  int ret = strncmp(p1, p2, 5);
  //d>q,只要你传大于等于4的数字,返回结果都是-1,也就是小于0的数字
  printf("ret=%d\n", ret);
  return 0;
}


这个函数的个数限制就没有上面那两个的花样多了,这也很好理解,只要两个字符的大小有差别,函数就直接返回值了,哪还管你比较的个数是多少啊,一旦对比有结果,你后面传再多的操作个数,返回结果都是一样的


11.内存函数memcpy的模拟实现

struct s
{
  char name[30];
  int age;
};
void* my_memcpy(void* dest, const void* src, int num)
{
  assert(dest&&src);
  void* ret = dest;
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest =(char*)dest+1;
    src = (char*)src + 1;
    /*++(char*)dest;
    ++(char*)src;*/
  }
  return ret;
}
int main()
{
  //void* memcpy(void* dest, const void* src, size_t count);第三个参数是拷贝字节数
  int arr1[] = { 1,2,3,4,5 };
  int arr2[5] = { 0 };
  //strcpy(arr2, arr1);
//1.当我们想将arr1数组内容拷贝到arr2时,strcpy函数是做不到的,但如果监视窗口看的话,是可以传1过去的
//2.01000000 02000000 03000000 04000000 05000000-内存中arr1数组以小端存储模式存放数组内容
//但strcpy会把01读取到,然后再读取00也就是\0那么就拷贝停止了
  struct s arr3[] = { {"张三",20} };
  struct s arr4[] = { {"李四",30} };
  my_memcpy(arr2, arr1, sizeof(arr1));
  my_memcpy(arr4, arr3, sizeof(arr3));
  return 0;
}



大家不要被这个函数吓到,其实这个函数就是原来的strncpy加上额外的功能,而已,我们只要掌握其中的重要思想,就可以实现这个函数了


其实这个代码的核心思想就是,我们内存拷贝函数是要操作不同的类型数据的,那么在拷贝时,指针的类型就有必要发生改变了,应该用一个可以接收任意地址的指针来作为参数,也就是void指针,而且我们接收了地址之后,想要将内容都拷贝过去,操作的字节数也是位置的,所以我们用将其强制转换为char指针,然后一个字节一个字节的去拷贝内容,这样就可完美模拟实现memcpy函数了


这里还要注意一个点(博主在写这个函数时,就遇到了这样的问题),下面我们来看一下这两段代码

*(char*)dest = *(char*)src;
dest =(char*)dest+1;
src = (char*)src + 1;
错误代码示范:   
++(char*)dest;
++(char*)src;


下面的代码如果单纯看,你可能觉得没什么问题,但如果我们看一下操作符优先级,或许就会发现问题了

6f7f3a77e00b4c0f816d938310311db0.jpeg


这里会先执行自增或自减运算符,可是,如果你没有先进行强转指针类型,就去对void*指针进行自增运算,这是不符合语法规定的,程序会出现错误



12.内存函数memmove的模拟实现

12.1C语言标准规定:


memcpy只要能够处理不重叠的内存拷贝即可

memmove既可以处理不重叠的内存拷贝,又可以处理重叠的内存拷贝(附加的功能实现)

12.2模拟实现一下memmove函数(处理内存重叠的情况)

void* my_memmove(void* dest, void* src, size_t num)
{
  assert(dest && src);
  void* ret = dest;
  if (src < dest)
  {
    while (num--)
    {
      *((char*)dest + num) = *((char*)src + num);
    }
  }
  else
  {
    while (num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;
      src = (char*)src + 1;
    }
  }
  return ret;
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr, arr+2, 20);//处理内存重叠的情况
//把12345拷贝到34567的位置,我们可以采用从后往前拷贝的方法,即先拷贝5到7,4到6,3到5,2到4,1到3
//如果把34567拷贝到12345的位置,我们可以采用从前往后拷贝的方法,即先拷贝3到1,4到2,5到3,6到4,7到5
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

当我们用了上面那种的拷贝思想之后,就不害怕我们重叠区域的数字覆盖了,就是数字被覆盖后我们无法继续原有的拷贝了,举个栗子:

1231af5255c44222820d84ba3395cfd2.png


所以我们这里也运用了一个重要思想,就是改变拷贝数字的顺序,消除掉数字被覆盖的这种问题隐患。


这里的代码实现我们也还是延续了之前memcpy的想法,就是一个字节一个字节的拷贝,而且由于src和dest位置的大小不同,我们实现代码的方式也是不同的,所以也要进行if和else的分支语句判断


13.内存函数memcmp(不做详细介绍,简单说一下函数的特点)

int main()
{
  //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 05 00 00 00 04 00 00 00 03 00 00 00
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 1,2,5,4,3 };
  int ret = memcmp(arr1, arr2, 9);
  printf("%d", ret);
  return 0;
}


值得注意的是这个memcmp函数和strncmp函数很相似,他们操作的都是字节个数,所以根据小端存储模式可知,ret是-1(vs环境下),第9个字节分别是03和05所以arr1是小于arr2的,返回小于0的值,vs环境下返回-1



三、总结:

3.1 不带n的四个函数

strlen,strcpy,strcmp,strcat都是不受操作个数限制的函数,他们的参数只有两个


3.2 带n的三个函数

strncmp,strncpy,strncat等函数的第三个参数都是操作的字节数,其中的strncpy,strncat具体的功能要和strcpy,strcat区分开来,函数细节上是有所不同的


3.3 四个内存操作函数


其中我们重点讲解了memmove和memcpy函数。memmove函数功能更加强大一些,它可以包括memcpy函数的功能


对于memset和memcmp函数我们只介绍了他的用法,并没有具体的模拟实现。


要记住的一个重要知识点是:内存操作函数操作的也是字节数,在传参时我们要注意这一点,在分析代码时也要注意这一点





相关文章
|
15天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
36 10
|
15天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
37 9
|
15天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
30 8
|
15天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
39 6
|
15天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
90 6
|
15天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
47 6
|
17天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
46 1
|
6月前
|
程序员 C语言 C++
【C语言基础】:动态内存管理(含经典笔试题分析)-2
【C语言基础】:动态内存管理(含经典笔试题分析)
|
6月前
|
程序员 编译器 C语言
【C语言基础】:动态内存管理(含经典笔试题分析)-1
【C语言基础】:动态内存管理(含经典笔试题分析)
|
Java 数据库连接 C语言
C语言进阶教程(内存分配常见问题分析)
C语言进阶教程(内存分配常见问题分析)
74 0