2014-07-22 13:54:59
strcpy_s和strcpy()函数的功能几乎是一样的。strcpy函数,就象gets函数一样,它没有方法来保证有效的缓冲区尺寸,所以它只能假定缓冲足够大来容纳要拷贝的字符串。在程序运行时,这将导致不可预料的行为。用strcpy_s就可以避免这些不可预料的行为。
这个函数用两个参数、三个参数都可以,只要可以保证缓冲区大小。
三个参数时:第一个参数为指针
1 errno_t strcpy_s( 2 char *strDestination, 3 size_t numberOfElements, 4 const char *strSource 5 );
两个参数时:第一个参数为数组
1 errno_t strcpy_s( 2 char (&strDestination)[size], 3 const char *strSource 4 ); // C++ only
例子:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 5 void Test(void) 6 { 7 char *str1=NULL; 8 str1=new char[20]; 9 char str[7]; 10 strcpy_s(str1,20,"hello world");//三个参数 11 strcpy_s(str,"hello");//两个参数但如果:char *str=new char[7];会出错:提示不支持两个参数 12 cout<<"strlen(str1):"<<strlen(str1)<<endl<<"strlen(str):"<<strlen(str)<<endl; 13 printf(str1); 14 printf("\n"); 15 cout<<str<<endl; 16 } 17 18 int main() 19 { 20 Test(); 21 return 0; 22 }
输出为:
1 strlen(str1): 11 //另外要注意:strlen(str1)是计算字符串的长度, 不包括字符串末尾的“\0”!!! 2 strlen(str): 5 3 hello world 4 hello
转自:strcpy_s函数的实现
代码是这样的:
1 _FUNC_PROLOGUE 2 errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC) 3 { 4 _CHAR *p; 5 size_t available; 6 7 /* validation section */ 8 _VALIDATE_STRING(_DEST, _SIZE); 9 _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE); 10 11 p = _DEST; 12 available = _SIZE; 13 while ((*p++ = *_SRC++) != 0 && --available > 0) 14 { 15 } 16 17 if (available == 0) 18 { 19 _RESET_STRING(_DEST, _SIZE); 20 _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE); //在这里提示错误,使用_CrtDbgReportW 21 } 22 _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1); 23 _RETURN_NO_ERROR; 24 }
也就是说当源串大于目地串时程序报错,是有点进步意义,但不能防止所有错误,在用字串函数时还得要非常小心。
在debug版下它最终由_CrtDbgReportW报错,在release版下呢?
从调用堆栈上看是先调用_invalid_parameter,然后调用_crt_debugger_hook,显示错误提示框(_crt_debugger_hook可以用自己的代码替换掉)
实际上_RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE)最终调用下面的代码:
1 #define _VALIDATE_RETURN_VOID( expr, errorcode ) / 2 { / 3 int _Expr_val=!!(expr); / 4 _ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); / 5 if ( !( _Expr_val ) ) / 6 { / 7 errno = errorcode; / 8 _INVALID_PARAMETER(_CRT_WIDE(#expr)); / 9 return; / 10 } / 11 }
_ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) );这句是在debug下用的,而release版下会调用 _INVALID_PARAMETER(_CRT_WIDE(#expr)); 可惜的是错误提示少了一些。
这种debug版与release版不同的处理方式好象已经成为了一个定规,这样真的好吗?像assert在release版下不报错,并不是一个好的方式,我还是比较喜欢重定义assert让它在release下也输出错误。