2013-07-05 14:07:49
本函数给出了几种strcpy与strncpy的实现,有ugly implementation,也有good implementation。并参考标准库中的implementation,最后给出了比较好的implementation。
字符串复制,一个一个字符进行判断,直到最后一个结束符。
问题:
*与++的优先级问题:
根据《C缺陷与C陷阱》上的说法,两者是同一个优先级,结合性是从右向左。但*p++的含义却是先得到*p的值,之后再对指针p加1,具体实例见文章:http://www.cnblogs.com/youngforever/p/3171283.html
因此,
while ( (*dst = *src) != '\0')
dst++;
*src++;
可简写为:
while ( (*dst = *src) != '\0') ;
又因为'\0'的ASCII值就是0,因此while ( (*dst++ = *src++) != '\0') ; 可简写为while ( *dst++ = *src++ ) ;
并注意到结束时字符串结束符'\0'已经复制,因为是先复制再判断的。
小结:
标准库函数并没有输入合法性检查,这将输入合法性检查的任务推给了函数的调用者。
对于strcpy函数,好的implementation要考虑一下几点:
-
- 函数src参数应为const,dst参数为非const;
- 函数要返回dst的地址,以方便嵌套使用该函数;
- 函数返回的dst的地址不要为const类型;
- 确定dst要有字符串结束符;
- 注意输入合法性检查注意输入合法性检查。
对于strncpy函数,除了以上几点外,好的implementation还要考虑一下几点:
当source的长度小于count时,应该怎么办?标准库函数的做法是,对dst大于source长度、小于count的部分赋值为\0;但在当source的长度大于count时。赋值到dst中的字符串是没有结束符的,在下面的运行结果中_strncpy_2的测试部分可以看到;这是因为代码中只这样写的:
1 while (count && (*dest++ = *source++)) /* copy string */ 2 count--; 3 4 if (count) /* pad out with zeroes */ 5 while (--count) 6 *dest++ = '\0';
这样在当source的长度大于或等于count时,就不会执行下面的if语句,而上面的while条件判断由于是先判断count的值,后复制的,因此,在count减为0时,刚好复制完最后一个有效字符,即 \0 的前一个字符,而没有复制\0.
如果要想使得source的长度大于count时,复制到dst中的字符串也有结束符,_strncpy_3函数可以做到这一点;
注意若while中的换为(*dest++ = *source++) && count,结果就会不同,即:
1 while ( (*dest++ = *source++) && count ) 2 count--;
这样写,就会在source长度大于count时,多复制一个字符,因为count为0时,*source不是\0,仍会惊醒复制曹组,之后才会判断count的值。
代码:
1 #include <iostream> 2 3 using namespace std; 4 #define SIZE 100 5 6 7 /*** 8 *char *strcpy(dst, src) - copy one string over another 9 * 10 *Purpose: 11 * Copies the string src into the spot specified by 12 * dest; assumes enough room. 13 * 14 *Entry: 15 * char * dst - string over which "src" is to be copied 16 * const char * src - string to be copied over "dst" 17 * 18 *Exit: 19 * The address of "dst" 20 * 21 *Exceptions: 22 *******************************************************************************/ 23 24 const char * _strcpy_1(char *dst,const char *src) 25 { 26 if (NULL == src || NULL == dst) 27 { 28 return NULL; 29 } 30 char *dst_ret = dst; 31 //const char *dst_ret = dst; 32 while ( (*dst++ = *src++) != '\0') ; //*的优先级高于++,结束时'\0'已经复制 33 return dst_ret; 34 } 35 36 char * _strcpy_2(char *dst,const char *src) 37 { 38 if (NULL == src || NULL == dst) 39 { 40 return NULL; 41 } 42 char *dst_ret = dst; 43 //const char *dst_ret = dst; //返回值不需要是const类型的 44 while ( (*dst++ = *src++) != '\0') ; //*的优先级高于++ 45 return dst_ret; 46 } 47 48 //标准库函数给出的implementation,没有输入合法性检查 49 char * _strcpy_3(char *dst,const char *src) 50 { 51 char *cp = dst; 52 53 while ( *cp++ = *src++ ) 54 ; /* Copy src over dst */ 55 56 return ( dst ); 57 } 58 59 //标准库函数给出的implementation,加上输入合法性检查 60 //好的implementation要考虑一下几点: 61 //1)函数src参数应为const,dst参数为非const 62 //2)注意输入合法性检查 63 char * _strcpy_4(char *dst,const char *src) 64 { 65 if (NULL == src || NULL == dst) 66 { 67 return NULL; 68 } 69 70 char *cp = dst; 71 72 while ( *cp++ = *src++ ) 73 ; /* Copy src over dst */ 74 75 return ( dst ); 76 } 77 78 /*** 79 *char *strncpy(dest, source, count) - copy at most n characters 80 * 81 *Purpose: 82 * Copies count characters from the source string to the 83 * destination. If count is less than the length of source, 84 * NO NULL CHARACTER is put onto the end of the copied string. 85 * If count is greater than the length of sources, dest is padded 86 * with null characters to length count. 87 * 88 * 89 *Entry: 90 * char *dest - pointer to destination 91 * char *source - source string for copy 92 * unsigned count - max number of characters to copy 93 * 94 *Exit: 95 * returns dest 96 * 97 *Exceptions: 98 * 99 *******************************************************************************/ 100 101 //不好的implementation 102 //因为参数count为int,且在src的长度小于count时,dst后面的没有处理 103 char * _strncpy_1(char *dst,const char *src,int count) 104 { 105 if (NULL == src || NULL == dst) 106 { 107 return NULL; 108 } 109 char *cp = dst; 110 int current_count = 0; 111 //while ( (*cp++ = *src++) != '\0' && current_count != count) 112 // ++current_count; 113 114 //*(cp - 1) = '\0'; //结束符 115 116 while ( current_count != count && (*cp++ = *src++) != '\0') 117 ++current_count; 118 119 *cp = '\0'; //结束符 120 121 return ( dst ); 122 } 123 124 //标准库函数的implementation 125 char * _strncpy_2 ( 126 char * dest, 127 const char * source, 128 size_t count 129 ) 130 { 131 char *start = dest; 132 133 while (count && (*dest++ = *source++)) /* copy string */ 134 count--; 135 136 if (count) /* pad out with zeroes */ 137 while (--count) 138 *dest++ = '\0'; //在sorce的长度小于count时,后面不补'\0',只是将source的前面count个字符覆盖,后面的不变 139 140 return(start); 141 } 142 143 //标准库函数的implementation,加上输入合法性检查 144 //好的implementation要考虑一下几点: 145 //1)输入source为const,dest为非const,count为size_t类型 146 //2)在sorce的长度小于count时,后面补'\0' 147 //3)在sorce的长度大于count时,同样有结束符'\0' 148 char * _strncpy_3 ( 149 char * dest, 150 const char * source, 151 size_t count 152 ) 153 { 154 if (NULL == source || NULL == dest) 155 { 156 return NULL; 157 } 158 159 char *start = dest; 160 161 while (count && (*dest++ = *source++)) /* copy string */ 162 count--; //若while中的换为(*dest++ = *source++) && count,结果就会不同 163 164 //*(dest - 1) = '\0'; //结束符 165 *dest = '\0'; //在sorce的长度小于count时,后面补'\0' 166 167 if (count) /* pad out with zeroes */ 168 while (--count) 169 *dest++ = '\0'; 170 171 return(start); 172 } 173 174 175 //测试程序 176 int main() 177 { 178 //_strcpy 179 char src_1[SIZE] = "hello world!"; 180 char dst[SIZE] = "\0"; 181 182 cout<<"test _strcpy_1..."<<endl; 183 cout<<"the src string is : "<<src_1<<endl; 184 _strcpy_1(dst,src_1); 185 cout<<"the dst string is : "<<dst<<endl; 186 187 char src_2[SIZE] = "hello!"; 188 cout<<"test _strcpy_2..."<<endl; 189 cout<<"the src string is : "<<src_2<<endl; 190 _strcpy_2(dst,src_2); 191 cout<<"the dst string is : "<<dst<<endl; 192 193 char src_3[SIZE] = "happy birthday!"; 194 cout<<"test _strcpy_3..."<<endl; 195 cout<<"the src string is : "<<src_3<<endl; 196 _strcpy_3(dst,src_3); 197 cout<<"the dst string is : "<<dst<<endl; 198 199 char *src_null = NULL; //不能这样赋值,报错:cannot convert from 'int' to 'char [100]' 200 //cout<<"test _strcpy_3(src == NULL )..."<<endl; 201 ////cout<<"the src string is : "<<src_3<<endl; 202 //_strcpy_3(dst,src_null); 203 //cout<<"the dst string is : "<<dst<<endl; 204 205 char src_4[SIZE] = "happy!"; 206 cout<<"test _strcpy_4..."<<endl; 207 cout<<"the src string is : "<<src_4<<endl; 208 _strcpy_4(dst,src_4); 209 cout<<"the dst string is : "<<dst<<endl; 210 211 cout<<"test _strcpy_4(src == NULL )..."<<endl; 212 _strcpy_4(dst,src_null); 213 cout<<"the dst string is : "<<dst<<endl; 214 215 //_strncpy 216 char dst_2[SIZE] = "\0"; 217 size_t count = 4; 218 cout<<"test _strncpy_1..."<<endl; 219 cout<<"the src string is : "<<src_1<<endl; 220 cout<<"the number to copy is : "<<count<<endl; 221 _strncpy_1(dst_2,src_1,count); 222 cout<<"the dst_2 string is : "<<dst_2<<endl; 223 224 count = 20; 225 cout<<"test _strncpy_1..."<<endl; 226 cout<<"the src string is : "<<src_1<<endl; 227 cout<<"the number to copy is : "<<count<<endl; 228 _strncpy_1(dst_2,src_1,count); 229 cout<<"the dst_2 string is : "<<dst_2<<endl; 230 231 count = 4; 232 cout<<"test _strncpy_2..."<<endl; 233 cout<<"the src string is : "<<src_1<<endl; 234 cout<<"the number to copy is : "<<count<<endl; 235 _strncpy_2(dst_2,src_1,count); 236 cout<<"the dst_2 string is : "<<dst_2<<endl; 237 238 count = 20; 239 cout<<"test _strncpy_2..."<<endl; 240 cout<<"the src string is : "<<src_1<<endl; 241 cout<<"the number to copy is : "<<count<<endl; 242 _strncpy_2(dst_2,src_1,count); 243 cout<<"the dst_2 string is : "<<dst_2<<endl; 244 245 count = 4; 246 cout<<"test _strncpy_3..."<<endl; 247 cout<<"the src string is : "<<src_1<<endl; 248 cout<<"the number to copy is : "<<count<<endl; 249 _strncpy_3(dst_2,src_1,count); 250 cout<<"the dst_2 string is : "<<dst_2<<endl; 251 252 count = 20; 253 cout<<"test _strncpy_3..."<<endl; 254 cout<<"the src string is : "<<src_1<<endl; 255 cout<<"the number to copy is : "<<count<<endl; 256 _strncpy_3(dst_2,src_1,count); 257 cout<<"the dst_2 string is : "<<dst_2<<endl; 258 259 return 0; 260 }
运行结果(分别测试count大于、以及小于source长度的情形):
1 test _strcpy_1... 2 the src string is : hello world! 3 the dst string is : hello world! 4 test _strcpy_2... 5 the src string is : hello! 6 the dst string is : hello! 7 test _strcpy_3... 8 the src string is : happy birthday! 9 the dst string is : happy birthday! 10 test _strcpy_4... 11 the src string is : happy! 12 the dst string is : happy! 13 test _strcpy_4(src == NULL )... 14 the dst string is : happy! 15 test _strncpy_1... 16 the src string is : hello world! 17 the number to copy is : 4 18 the dst_2 string is : hell 19 test _strncpy_1... 20 the src string is : hello world! 21 the number to copy is : 20 22 the dst_2 string is : hello world! 23 test _strncpy_2... 24 the src string is : hello world! 25 the number to copy is : 4 26 the dst_2 string is : hello world! 27 test _strncpy_2... 28 the src string is : hello world! 29 the number to copy is : 20 30 the dst_2 string is : hello world! 31 test _strncpy_3... 32 the src string is : hello world! 33 the number to copy is : 4 34 the dst_2 string is : hell 35 test _strncpy_3... 36 the src string is : hello world! 37 the number to copy is : 20 38 the dst_2 string is : hello world! 39 请按任意键继续. . .
从运行结果可以看出,