返回列表 发新帖我要提问本帖赏金: 20.00元(功能说明)

[C语言] 深入解析单片机复制函数

[复制链接]
893|0
 楼主| abner_ma 发表于 2023-2-23 18:40 | 显示全部楼层 |阅读模式
     在进行程序设计时,免不了在函数之间进行数据复制。常见的方法主要有:
函数原型:
1.char *strcpy(char* dest, const char *src)   
  把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
  1.1.strcpy函数在拷贝时也会把源字符串中的'\0'拷贝到目标字符串中作为拷贝结束的标志。
  1.2.目标空间必须足够大存放的下源字符串,放不下源字符串中的内容,程序会崩溃。
   原型:


  1. //strcpy(dest,src)把从src地址开始且含有null结束符的字符串复制到以dest开始的地址空间
  2. char *strcpy(char *strDest, const char *strSrc)
  3. {
  4.   char *res=strDest;
  5.   assert((strDest!=NULL)&&(strSrc!=NULL));
  6.   while((*strDest=*strSrc)!='\0')
  7.   {
  8.     strDest++;
  9.     strSrc++;
  10.   }
  11.   return res;
  12. }


2.void *memcpy(void *dest, const void *src, size_t n);
  从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
  window下函数原型:
  1. void* __cdecl memcpy(void* dst,const void* src,size_t count)
  2. {
  3.     void*ret=dst;
  4.   #if defined(_M_MRX000)||defined(_M_ALPHA)||defined(_M_PPC)
  5.   {
  6.        extern void RtlMoveMemory(void *,const void *,size_t count);
  7.        RtlMoveMemory(dst,src,count);
  8.   }
  9.   #else /*defined(_M_MRX000)||defined(_M_ALPHA)||defined(_M_PPC)*/
  10.   /*
  11.   *copy from lower addresses to higher addresses
  12.   */
  13.   while(count--){
  14.       *(char *)dst = *(char *)src;
  15.       dst = (char *)dst+1;
  16.       src = (char *)src+1;
  17.   }
  18.   #endif  /*defined(_M_MRX000)||defined(_M_ALPHA)||defined(_M_PPC)*/
  19.   return (ret);
  20. }
  linux系统下:
  1. void *memcpy(void *to, const void *from, size_t n)
  2. {   //记录拷贝目的位置,为了返回拷贝内容的首地址
  3.     void *xto = to;
  4.     size_t temp, temp1;
  5.     //判断拷贝的字节数
  6.     if (!n)
  7.         return xto;   //
  8.     if ((long)to & 1) {
  9.         char *cto = to;
  10.         const char *cfrom = from;
  11.         *cto++ = *cfrom++;
  12.         to = cto;
  13.         from = cfrom;
  14.         n--;
  15.     }
  16.     if (n > 2 && (long)to & 2) {
  17.         short *sto = to;
  18.         const short *sfrom = from;
  19.         *sto++ = *sfrom++;
  20.         to = sto;
  21.         from = sfrom;
  22.         n -= 2;
  23.     }
  24.     temp = n >> 2;
  25.     if (temp) {
  26.         long *lto = to;
  27.         const long *lfrom = from;
  28.   #if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
  29.         for (; temp; temp--)
  30.             *lto++ = *lfrom++;
  31.   #else
  32.         asm volatile (
  33.             "    movel %2,%3\n"
  34.             "    andw  #7,%3\n"
  35.             "    lsrl  #3,%2\n"
  36.             "    negw  %3\n"
  37.             "    jmp   %%pc@(1f,%3:w:2)\n"
  38.             "4:    movel %0@+,%1@+\n"
  39.             "    movel %0@+,%1@+\n"
  40.             "    movel %0@+,%1@+\n"
  41.             "    movel %0@+,%1@+\n"
  42.             "    movel %0@+,%1@+\n"
  43.             "    movel %0@+,%1@+\n"
  44.             "    movel %0@+,%1@+\n"
  45.             "    movel %0@+,%1@+\n"
  46.             "1:    dbra  %2,4b\n"
  47.             "    clrw  %2\n"
  48.             "    subql #1,%2\n"
  49.             "    jpl   4b"
  50.             : "=a" (lfrom), "=a" (lto), "=d" (temp), "=&d" (temp1)
  51.             : "0" (lfrom), "1" (lto), "2" (temp));
  52.   #endif
  53.         to = lto;
  54.         from = lfrom;
  55.       }
  56.      if (n & 2) {
  57.         short *sto = to;
  58.         const short *sfrom = from;
  59.         *sto++ = *sfrom++;
  60.         to = sto;
  61.         from = sfrom;
  62.       }
  63.       if (n & 1) {
  64.           char *cto = to;
  65.           const char *cfrom = from;
  66.           *cto = *cfrom;
  67.       }
  68.     return xto;
  69. }
   strcpy和memcpy主要有三方面的区别。

其一:复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
其二:复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,如果空间不够,就会引起内存溢出。memcpy则是根据其第3个参数决定复制的长度。
其三:用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy,由于字符串是以“\0”结尾的,所以对于在数据中包含“\0”的数据只能用memcpy。

3.void *memmove( void* dest, const void* src, size_t count );   由src所指内存区域复制count个字节到dest所指内存区域。
4.void *memset(void *s, int ch,  size_t  n);   将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s。
5.int sprintf(char *str, const char *format, ...);
功能:将格式化的数据写入某个字符串缓冲区
入参:format,输出字符串的格式化列表,比如%d、%s、%c等
入参:format对应的不定参数列表,与printf类似
出参:buffer,指向一段存储空间,用于存储格式化之后的字符串
返回值:返回写入buffer的字符数,出错则返回-1

5.1. 根据参数[format字符串]来转换并格式化数据
5.2. 将格式化结果复制到str指向的字符串数组
5.3. 直到出现字符串结束符['\0']为止

    //sprintf用法举例

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5.     char buffer[10];
  6.     const int a = 12345;
  7.     const char *b = "102938465839202";
  8.    
  9.     sprintf(buffer, "%d", a);  //将变量a按int类型打印成字符串,输出到buffer中
  10.     sprintf(buffer, "%d+%s", a, b);  //将变量a和字符串b连接成一个字符串输出到buffer中
  11.    
  12.     return 0;
  13. }
  程序分析:
  如果输出到buffer的内容长度不超过10个字节,那么此时sprintf的操作是没有风险的;
  如果超过了10个字节,那么就会导致buffer存储空间溢出,从存储位置上分析,buffer空间是一个栈空间,在它自己10个字节以外的空间是其他栈变量的存储空间,一旦sprintf将10字节外的其他空间也操作了,这就有可能破坏了其他栈变量的内容,程序崩溃发生。


6.int snprintf(char *str, size_t size, const char *format, ...);
6.1. 根据参数[format字符串]来转换并格式化数据
6.2. 将格式化结果复制到str指向的字符串数组
6.3. 直到出现字符串结束符['\0']或达到size指定大小为止(截断)
  1. //snprintf用法
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. int main()
  5. {
  6.     char buffer[10];
  7.     const int a = 12345;
  8.     const char *b = "102938465839202";
  9.    
  10.     snprintf(buffer, sizeof(buffer), "%d", a);  //将变量a按int类型打印成字符串,输出到buffer中
  11.     snprintf(buffer, sizeof(buffer), "%d+%s", a, b);  //将变量a和字符串b连接成一个字符串输出到buffer中
  12.    
  13.     return 0;
  14. }
分析:
1. windows上无snprintf,但是有_snprintf可以达到同样的功能,但是细节之处略有不同
  windows下没有snprintf相关的库函数。
  解决方案:声明snprintf在windows下对应的库函数。


  1. #ifdef _WIN32
  2.         //#define snprintf _snprintf
  3. #endif
  4. #ifdef _MSC_VER
  5.         #define snprintf _snprintf
  6. #endif


2. 未溢出时,sprintf和snprintf都会在字符串末尾加上'\0';
3. 超出缓冲区大小时,_snprintf不会造成溢出,只是不会在缓冲区中添加字符结束符
4. sprintf始终会在字符串末尾添加结束符,但是存在缓冲区溢出的情况
5. _snprintf比sprintf安全,即不会造成缓冲区溢出


打赏榜单

21ic小管家 打赏了 20.00 元 2023-03-13
理由:签约作者奖励

您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

104

主题

191

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部