【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天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
18天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
18天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
24天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
24天前
|
存储 大数据 C语言
C语言 内存管理
本文详细介绍了内存管理和相关操作函数。首先讲解了进程与程序的区别及进程空间的概念,接着深入探讨了栈内存和堆内存的特点、大小及其管理方法。在堆内存部分,具体分析了 `malloc()`、`calloc()`、`realloc()` 和 `free()` 等函数的功能和用法。最后介绍了 `memcpy`、`memmove`、`memcmp`、`memchr` 和 `memset` 等内存操作函数,并提供了示例代码。通过这些内容,读者可以全面了解内存管理的基本原理和实践技巧。
|
24天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
24天前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
24天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
1月前
|
机器学习/深度学习 编译器 Serverless
C语言中函数
C语言中函数
21 0
|
1月前
|
存储 NoSQL 程序员
C语言中的内存布局
C语言中的内存布局
35 0

热门文章

最新文章