atoi,itoa,strcpy, strcmp,strcpy, strcpy_s, memc...

不问归期 提交于 2019-12-09 10:30:51

strcpy()、strlen()、memcpy()、memmove()、memset()的实现


strcpy(), 字符串拷贝.
char *strcpy(char *strDest, const char *strSrc)
{
    assert((strDest!=NULL) && (strSrc !=NULL));
    char *address = strDest;     
    while( (*strDest++ = * strSrc++!= '\0'
        NULL ; 
    return address ;
}


strlen,

第一种方法:
int strlen( const char *str)
{
    assert(str != NULL);
     int len = 0;
     while((*str++) !=  '\0')
        len++;
     return len;
}
第二种方法:
int strlen( const char *str)
{
    assert(str != NULL);
     const char *p = str;
     while((*p++) !=  '\0');
     return p - str - 1;
}
第三种方法:
int strlen( const char* str)
{
     if (str[0] ==  '\0')
         return 0;
     else
         return strlen((char *)(&str[0]+1))+1;
}


memcpy, 拷贝不重叠的内存块 
 void *memcpy(void* pvTo, void* pvFrom, size_t count) 
 {
     assert(pvTo != NULL && pvFrom != NULL);
     byte* pbTo = (byte*)pvTo;
     byte* pbFrom = (byte*)pvFrom;
     
     assert(pbTo>=pbFrom+
count || pbFrom>=pbTo+count);
     while(
count-- > )
         *pbTo++ == *pbFrom++;
     return pvTo;
}


void* memmove(void *dest, const void *src,size_t count
{ 
     if (
count == return 
     if (dest == NULL) return 
     if (src == NULL)    return 
     char *psrc = (char*)src; 
     char *pdest = (char*)dest; 
     if((pdest <= psrc)  ||  (pdest >= psrc +
count))  
     {
        
 
         while (count--)
             *pdest++ = *psrc++;
     }
 
     else 
     {
        
 
         pdest += count - 1;
         psrc +=
 count - 1;
         while (
count--)
             *pdest-- = *psrc--;
     }
 
     return dest;
}


memset:把buffer所指内存区域的前count个字节设置成字符c
void * memset(void* buffer, int c, int count)
{
    char* pvTo=(char*)buffer;
    assert(buffer != NULL);
    while(count-- > )
        *pvTo++=(char)c;
    return buffer;
}


分析:
memmove、memcpy和memccpy三个函数都是内存的拷贝,从一个缓冲区拷贝到另一个缓冲区。
memmove(void *dest,void*src,int count)
memcpy(void *dest,void *src,int count)
memccpy(void*dest,void*src,int ch,int count)
表头文件: #include <string.h>
定义函数: void *memcpy(void *dest, const void *src, size_t n)
函数说明: memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束
返回值:   返回指向dest的指针
表头文件: #include <string.h>
定义函数: void *memccpy(void *dest, const void *src, int c, size_t n);
函数说明: memccpy()用来拷贝src所指的内存内容前n个字节到dest所指的地址上。与memcpy()不同的是,memccpy()如果在src中遇到某个特定值(int c)立即停止复制。
返回值:   返回指向dest中值为c的下一个字节指针。返回值为0表示在src所指内存前n个字节中没有值为c的字节。
表头文件: #include <string.h>
定义函数: void *memmove(void *dest, const void *src, size_t n);
函数说明:memmove()是从一个缓冲区移动到另一个缓冲区中。 
返回值:   返回指向dest指针。
当dest <= src-count 或dest >= src+count时,以上三个函数均不会产生覆盖问题,即源数据不会被更改。
若不在以上范围内,则源数据会被更改。 不妨用个小程序来测一下:

# include <string.h>
# include <stdio.h>

int main()
{
    int i = 0;
    int a[10];
 
    for(i; i < 10; i++)
    {
        a[i] = i;
   }

   memcpy(&a[4], a, sizeof(int)*6);
 
   for(i = 0; i < 10; i++)
   {
       printf("%d ",a[i]);
   }
 
   printf("\n");
   return 0;
 }
 
将上面代码gcc之后再运行,结果为:0 1 2 3 0 1 2 3 0 1 。
再把第13行改成:memmove(&a[4], a, sizeof(int)*6),重新gcc再运行,结果为:0 1 2 3 0 1 2 3 4 5 !
呵呵,两者的区别出现了。不过其实这样还不够,继续修改13行: memmove(a, &a[4], sizeof(int)*6) //也就是将源、目的置换一下而已。重新gcc编译再运行,结果为:4 5 6 7 8 9 6 7 8 9 。
还不够,继续修改13行为: memcpy(a, &a[4], sizeof(int)*6); gcc并运行,结果仍为: 4 5 6 7 8 9 6 7 8 9 !
至此真相已经大白了。对比上面四个结果,不难得出以下结论:
1. 当 src 和 dest 所指内存区有重叠时,memmove 相对 memcpy 能提供保证:保证能将 src 所指内存区的前 n 个字节正确的拷贝到 dest 所指内存中;
2. 当 src 地址比 dest 地址低时,两者结果一样。换句话说,memmove 与 memcpy 的区别仅仅体现在 dest 的头部和 src 的尾部有重叠的情况下;


_____________________________
1.//整数转换成字符串itoa函数的实现 
#include "stdafx.h"

#include <iostream>

using namespace std;

void itoaTest(int num,char str[] )

{

       int sign = num,i = 0,j = 0;

       char temp[11];

       if(sign<0)//判断是否是一个负数

       {

              num = -num;

       };

       do

       {

              temp[i] = num%10+'0';        

              num/=10;

              i++;

       }while(num>0);

       if(sign<0)

       {

              temp[i++] = '-';//对于负数,要加以负号

       }

       temp[i] = '\0';

       i--;

       while(i>=0)//反向操作

       {

              str[j] = temp[i];

              j++;

              i--;

       }

       str[j] = '\0';

}

2. //字符串转换成整数atoi函数的实现

int atoiTest(char s[])

{

       int i = 0,sum = 0,sign;    //输入的数前面可能还有空格或制表符应加判断

       while(' '==s[i]||'\t'==s[i])

       {

              i++;

       }

       sign = ('-'==s[i])?-1:1;

       if('-'==s[i]||'+'==s[i])

       {

              i++;

       }

       while(s[i]!='\0')

       {

              sum = s[i]-'0'+sum*10;

              i++;

       }    

       return sign*sum;

}

3.//字符串拷贝函数

#include "stdafx.h"

#include <assert.h>

#include <string.h>

#include <iostream>

using namespace std;

char *srcpy(char *dest,const char *source)

{

       assert((dest!=NULL)&&(source!=NULL));

       char *address = dest;

       while(*source!='\0')

       {

              *dest++=*source++;

       }

       *dest = '\0';

       return address;

}

 4.//判断输入的是否是一个回文字符串

#include "stdafx.h"

#include <string.h>

#include <iostream>

using namespace std;

//方法一:借助数组

bool isPalindrome(char *input)

{

       char s[100];

       strcpy(s,input);

       int length = strlen(input);

       int begin = 0,end = length-1;

       while(begin<end)

       {

              if(s[begin]==s[end])

              {

                     begin++;

                     end--;

              }

              else

              {

                     break;

              }           

       }

       if(begin<end)

       {

              return false;

       }    

       else

       {

              return true;

       }     

}

//方法二:使用指针

bool isPalindrome2(char *input)

{

       if(input==NULL)

              return false;

       char *begin = input;

       char *end = begin+strlen(input)-1;

       while(begin<end)

       {

              if(*begin++!=*end--)

                     return false;

       }

       return true;

}

 int main(int argc, char* argv[])

{

       char *s ="1234554321";

       if(isPalindrome(s))

       {

              cout<<"True"<<endl;

       }

       else

       {

              cout<<"Fasle"<<endl;

       }

        if(isPalindrome2(s))

       {

              cout<<"True"<<endl;

       }

       else

       {

              cout<<"Fasle"<<endl;

       }

       cin.get();

        return 0;

}

5.//不使用库函数,编写函数int strcmp(char *source, char *dest),若相等返回0,否则返回-1

int strcmp(char *source, char *dest)

{

       assert(source != NULL && dest != NULL);

       while(*source++==*dest++)

       {

              if(*source=='\0'&&*dest=='\0')

                     return 0;        

       }

       return -1;

}



——————————————————————————————

一:strcmp,_stricmp
strcmp("ha","he")是可以的。
但是如下代码: 
string   str1   =   "ha"; 
string   str2   =   "he"; 
strcmp(str1,   str2); 
会产生如下的ERROR:
因为:
1)int   strcmp(const   char*   str1,   const   char*   str2); 
这个函数是C标准库的函数,处理的是C风格0结尾字符数组字符串。 
C++标准库中的string类有可以直接使用的<,>,<=,>=,==,!=运算符,通常也用不到这个函数
或者说:   strcmp()处理的是C风格的字符串。 而你用的是string类,2者是不同的。

但可以通过string成员函数string::c_str()转换成char*类型。象这样调用:strcmp(str1.c_str(),   str2.c_str())  

int ret = strcmp("aa", "ad"); //-1
int ret = _stricmp("aa", "ad");//-3
int ret = _stricmp("Aa","aD"); //-3
ret = strcmp("ad", "aa"); //1

ret = strcmp("aa", "aa"); //0
ret = _stricmp("aa", "AA"); //0

ret = strcmp("aa", "aad"); //-1

strcmp(s1,s2)与_stricmp(s1,s2)
相同点:处理的都是C格式的字串,如果比较C++格式的string类型字串时,可以使用string.c_str()来转换成C格式的字串再进行比较。
不同点:1)前者区分大小写,后者不区分大小写
              2)前者返回-1,0,1,后者返回字串实际差值,即整形数:负数,零,正数


二:strcpy_s&&strcpy的比较
如下所示:
char szBuf[2] = {0};
strcpy_s(szBuf, 2, "12131"); //注意第二个参数是申清空间的大小
strcpy(szBuf, "12131");   
上述代码,明显有缓冲区溢出的问题。 使用strcpy_s函数则会抛出一个异常。而使用strcpy函数的结果

则未定,因为它错误地改变了程序中其他部分的内存的数据,可能不会抛出异常但导致程序数据错误,

也可能由于非法内存访问抛出异常。使用新的增强安全的CRT函数有什么好处呢?简单地说,新的函数

加强了对参数合法性的检查以及缓冲区边界的检查,如果发现错误,会返回errno或抛出异常。

老版本的这些CRT函数则没有那么严格的检查与校验,如果错误地传输了参数或者缓冲区溢出,

那么错误并不能被立刻发现,对于定位程序错误也带来更大困难。

//--------------------strcpy,strcpy_s------------------------------------------

//原型:extern char *strcpy(char *dest,char *src);
//功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
//说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串,返回指向dest的指针。

//strcpy用法:
// 若源长<目标长 --->OK。
//将源串COPY至目标串(包括结尾符),其它字节保持不变,
//若源长>目标长 --->溢出报错。
//将源串COPY至目标串(包括结尾符),但目标串太短,下标越界而溢出,不正常的字串显然会在导致运行时异常,
//使用原则总结:使用时保证源长<目标长

//strcpy_s用法:
//若源长<目标长 sizeof([min, max])--->OK,但自定义拷贝的字串长度超过目标长度时,会因下标越界而导致运行异常
//若源长>目标长 sizeof任何一方都会发生运行异常,
//(有可能是编译器在copy前会先检查两者长度,
//如果源串较长便会发生告警而退出,因此后续使用sizeof(strlen(strDest)=0)语句已无效)
//使用原则总结: 1,使用时保证:源长<目标长,2,长度不能超出szDest长度,建议使用sizeof(szDest)

#include <iostream>
#include <string>
using namespace std;

#define STRCPY_TEST 0
#define LEN_STR 15

void main()
{
char szDest[10];
char szSrc_shorter[] = "1234";
char szSrc_longer[] ="123456789012";

cout<<"char szDest[10];"<<endl;
cout<<"char szSrc_shorter[] = \"1234\";"<<endl;
cout<<"char szSrc_longer[] = \"123456789012345\";"<<endl;

#if (0 == STRCPY_TEST)
cout<<"\n[OK] strcpy(szDest, szSrc_shorter);\nstrDest[1~15] = ";
strcpy(szDest, szSrc_shorter);
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;

strcpy(szDest, szSrc_longer);
cout<<"\n[NG] strcpy(szDest, szSrc_longer);\nstrDest[1~15] = ";
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;

#else

cout<<"\n[OK] strcpy_s(szDest, sizeof(szSrc_shorter), szSrc_shorter);\nstrDest[1~15] = ";
strcpy_s(szDest, sizeof(szSrc_shorter), szSrc_shorter);
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;

cout<<"[OK] strcpy_s(szDest, sizeof(szDest), szSrc_shorter);\nstrDest[1~15] = ";
strcpy_s(szDest, sizeof(szDest), szSrc_shorter); 
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;


cout<<"[NG] strcpy_s(szDest, sizeof(szSrc_longer), szSrc_longer);\nstrDest[1~15] = ";
strcpy_s(szDest, sizeof(szSrc_longer), szSrc_longer); 
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;

cout<<"\n[NG] strcpy_s(szDest, sizeof(szDest), szSrc_longer);\nstrDest[1~15] = ";
strcpy_s(szDest, sizeof(szDest), szSrc_longer); 
szDest[sizeof(szDest) - 1] = 0;
for(int i = 0; i < LEN_STR; i++)
{
   cout<<szDest[i]<<", ";
}
cout<<endl<<"szDest = "<<szDest<<endl;

#endif

}

输出:(NG部分的输出已无意义,可以不看)

(1)STRCPY_TEST==0
char szDest[10];
char szSrc_shorter[] = "1234";
char szSrc_longer[] = "123456789012345";
[OK] strcpy(szDest, szSrc_shorter);
strDest[1~15] = 1, 2, 3, 4, , ? ? ? ? ? ? ? ? ? ?

szDest = 1234
[NG] strcpy(szDest, szSrc_longer);
strDest[1~15] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, , ? ?

(2)STR_CPY_TEST<>0
char szDest[10];
char szSrc_shorter[] = "1234";
char szSrc_longer[] = "123456789012345";

[OK] strcpy_s(szDest, sizeof(szSrc_shorter), szSrc_shorter);
strDest[1~15] = 1, 2, 3, 4, , ? ? ? ? ? ? ? ? ? ?

szDest = 1234

[OK] strcpy_s(szDest, sizeof(szDest), szSrc_shorter);
strDest[1~15] = 1, 2, 3, 4, , ? ? ? ? ? ? ? ? ? ?

szDest = 1234

[NG] strcpy_s(szDest, sizeof(szSrc_longer), szSrc_longer);
strDest[1~15] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, , ? ?

szDest = 123456789012

[NG] strcpy_s(szDest, sizeof(szDest), szSrc_longer);
strDest[1~15] = 请按任意键继续. . .

//-------------------------------memcpy-------------------------------------
// 原型:extern void *memcpy(void *dest, void *src, unsigned int count);
// 原理:由src所指内存区域复制count个字节到dest所指内存区域。
// 说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
//strncpy() 是处理的字符串(遇零结束),
//memcpy() 是处理一个缓冲区(void*类型的),
//以字串类型的memcpy用法为例:
//源长<目标长时,第3个参数使用任何一方的size,效果都一样
//源长>目标长时,第3个参数用sizeof(strSrc),会造成下标越界,而使目标串溢出,从而导致运行异常
//                       第3个参数用sizeof(strDest), 末尾字符(\0)没有被COPY,输出字串会有乱码,
//                       但不会运行异常,如果不考虑完整性,可以使用strDest[sizeof(strDest)-1]=0截取前半部
//使用原则总结:1)保证源长<目标长;2)第3个参数推荐使用sizeof(strDest) 另外如果字串是指针要考虑一下sizeof的副作用

#include <iostream>
#include <string>
using namespace std;

void main()
{
char szSrc_shorter[] = "Good";
char szSrc_longer[] = "Have a Good Day!";
char szDest[10];

cout<<"char szSrc_shorter[] = \"Good\";"<<endl;
cout<<"char szSrc_longer[] = \"Have a Good Day!\";"<<endl;
cout<<"char szDest[10];"<<endl<<endl;

memcpy(szDest, szSrc_shorter, sizeof(szSrc_shorter));//<=>
//memcpy(szDest, szSrc_shorter, sizeof(szDest)); 
cout<<"[OK] memcpy(szDest, szSrc_shorter, sizeof(szSrc_shorter));";
cout<<"<=>\n[OK] memcpy(szDest, szSrc_shorter, sizeof(szDest));\nszDest[0~20] = \n";
for (int i = 0; i < 20; i++)
   cout<<szDest[i]<<", ";
cout<<endl<<"szDest = "<<szDest<<endl;

memcpy(szDest, szSrc_longer, sizeof(szDest));
cout<<"\n[OK] memcpy(szDest, szSrc_longer, sizeof(szDest));\nszDest[sizeof(szDest) - 1] = 0; \nszDest[0~20] = \n";
szDest[sizeof(szDest) - 1] = 0;
for (int i = 0; i < 20; i++)
   cout<<szDest[i]<<", ";
cout<<endl<<"szDest = "<<szDest<<endl;

memcpy(szDest, szSrc_longer, sizeof(szSrc_longer));
cout<<"\n[NG] memcpy(szDest, szSrc_longer, sizeof(szSrc_longer));\nszDest[sizeof(szDest) - 1] = 0; \nszDest[0~20] = \n";
szDest[sizeof(szDest) - 1] = 0;
for (int i = 0; i < 20; i++)
   cout<<szDest[i]<<", ";
cout<<endl<<"szDest = "<<szDest<<endl;

}


输出:(NG部分的输出没有意义,可以不看):

char szSrc_shorter[] = "Good";
char szSrc_longer[] = "Have a Good Day!";
char szDest[10];

[OK] memcpy(szDest, szSrc_shorter, sizeof(szSrc_shorter));<=>
[OK] memcpy(szDest, szSrc_shorter, sizeof(szDest));
szDest[0~20] =
G, o, o, d, , ?.....?(15个?)

szDest = Good

[OK] memcpy(szDest, szSrc_longer, sizeof(szDest));
szDest[sizeof(szDest) - 1] = 0;
szDest[0~20] =
H, a, v, e, , a, , G, o, , ? ...?(10个?)

szDest = Have a Go

[NG] memcpy(szDest, szSrc_longer, sizeof(szSrc_longer));
szDest[sizeof(szDest) - 1] = 0;
szDest[0~20] =
H, a, v, e, , a, , G, o, , d, , D, a, y, !, , ? ? ?
szDest = Have a Go

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!