一、最经典的题目:
        已知strcpy函数的原型是:char * strcpy(char * strDest,const char * strSrc);实现。
 
char * strcpy(char * strDest,const char * strSrc)
{
        if ((strDest==NULL)||(strSrc==NULL))  //[1]
        throw "Invalid argument(s)";              //[2]
        char * strDestCopy=strDest;             //[3]
        while ((*strDest++=*strSrc++)!='\0'); //[4]
                return strDestCopy;
}
        strcpy,拷贝字符串了,它遇到'\0'就结束拷贝
 1、注意的地方:
    [1]. 注重代码的健壮性, 检查指针的有效性
        A. 检查指针的有效性时若使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),其中存在着隐形转化,将char *转换为bool,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。
        B. 检查指针的有效性时若使用((strDest==0)||(strSrc==0)),直接使用字面常量(如本例中的0)会减少程序的可维护性。
    [2]. 错误发生情况下如何处理
        A. return new string("Invalid argument(s)");,从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。
        B.  return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。
    [3].保存原始的strDest值
    [4].循环的写法,注意边界条件
        循环写成while (*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上'\0'
2、返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
    链式表达式的形式如:
        int iLength=strlen(strcpy(strA,strB));
    又如:
        char * strA=strcpy(new char[10],strB);
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回,类型不符,编译报错。
 
二、类似的我们可以写出一个10分的strlen函数
int strlen( const char *str )
//输入参数const
{
         assert( strt != NULL );
         //断言字符串地址非0
         int len;
         while( (*str++) != '\0' )
         {    
                        len++;
         }
         return len;
}
三、strncpy、strlcpy
        A、strcpy的安全版本,char *strncpy(char *s1, const char *s2, size_t n);
        标准规定 n 并不是 sizeof(s1),而是要复制的 char 的个数。一个最常见的问题,就是 strncpy 并不帮你保证 \0 结束。
        char buf[8];        strncpy( buf, "abcdefgh", 8 );
        看这个程序,buf 将会被 "abcdefgh" 填满,但却没有 \0 结束符了。
        另外,如果 s2 的内容比较少,而 n 又比较大的话,strncpy 将会把之间的空间都用 \0 填充。这又出现了一个效率上的问题,如下:
        char buf[80];        strncpy( buf, "abcdefgh", 79 );
        上面的 strncpy 会填写 79 个 char,而不仅仅是 "abcdefgh" 本身。
        strncpy 的标准用法为:(手工写上 \0)
                strncpy(path, src, sizeof(path) - 1);
                path[sizeof(path) - 1] = '\0';
                len = strlen(path);
        B、strlcpy(char *dst, const char *src, size_t siz);
        使用 strlcpy,不需要我们手动负责 \0 了,仅需要把 sizeof(dst) 告之 strlcpy 即可:
                strlcpy(path, src, sizeof(path));
                len = strlen(src);
                if ( len >= sizeof(path) )
                       printf("src is truncated.");
        并且 strlcpy 传回的是 strlen(str),因此我们也很方便的可以判断数据是否被截断。
 
四、Memset 
        Memset  用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘\0’;
        例:char a[100];memset(a, '\0', sizeof(a));
     memset可以方便的清空一个结构类型的变量或数组。
如:
        struct sample_struct
        {
                 char   csName[16];
                 int    iSeq;
                 int    iType;
        };
        对于变量        struct sample_strcut  stTest;
        一般情况下,清空stTest的方法:
                stTest.csName[0]='\0';
                stTest.iSeq=0;
                stTest.iType=0;
用memset就非常方便:        memset(&stTest,0,sizeof(struct sample_struct));
如果是数组:         struct sample_struct   TEST[10];
        则        memset(TEST,0,sizeof(struct sample_struct)*10);
五、memcpy、memmove、memccpy
        内存的拷贝,从一个缓冲区拷贝到另一个缓冲区
        A、void *memcpy(void *dest, const void *src, size_t n)
        memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束,返回指向dest的指针
        B、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的字节。
        C、void *memmove(void *dest, const void *src, size_t n);
memmove()是从一个缓冲区移动到另一个缓冲区中。返回指向dest指针。
        D、当dest <= src-count 或dest >= src+count时,以上三个函数均不会产生覆盖问题,即源数据不会被更改。若不在以上范围内,则源数据会被更改。
如:                   char a[]={'a','b'};
                        char b[]={'c','d','e','f','g','h'};
                        memmove(a,b,sizeof(b));
      或是直接char *p=b+2;memmove(p,b,sizeof(b));
        输出数据会发现b中数据输出已被更改。
        发现即使a数组指向的空间不够存储数据,也能够移动成功。
        原因|dest - src |<count
        如果在使用这些函数时,分配给足够的空间,然后再使用就不会出现覆盖问题。也就是说如果外部分配给的空间不足以存储要拷贝的数据时,就有可能出现源数据被覆盖更改的问题。
        E、memcpy的实现