关于strcpy_s的使用
strcpy_s是strcpy的安全版本,它之所以安全,是因为其在拷贝字符串的时候会有越界的检查工作。以下是strcpy_s的实现代码,在tcscpy_s.inl文件可以找到:
/*** *tcscpy_s.inl - general implementation of _tcscpy_s * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * This file contains the general algorithm for strcpy_s and its variants. * ****/ _FUNC_PROLOGUE errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC) { _CHAR *p; size_t available; /* validation section */ _VALIDATE_STRING(_DEST, _SIZE); _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE); p = _DEST; available = _SIZE; while ((*p++ = *_SRC++) != 0 && --available > 0) { } if (available == 0) { _RESET_STRING(_DEST, _SIZE); _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE); } _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1); _RETURN_NO_ERROR; }
_VALIDATE_STRING应该是验证字符串的合法性,是否以null结尾。
_VALIDATE_POINTER_RESET_STRING应该是记录字符串的原始信息,以便拷贝失败以后恢复。
当目的地空间不够时,会根据_VALIDATE_POINTER_RESET_STRING记录的信息恢复字符串,并且(在Debug模式下)以弹出对话框的形式报告错误。
_FILL_STRING完成在字符串最后加上null结束符的工作。以前没有注意到这一点,所以犯了一个以下的错误,先看源代码:
const int ALLOC_GRANULARITY = 64 * 1024; //分配粒度:64K //随机产生一个指定范围内的随机数 inline int RandomGen(int nMin, int nMax) { return (rand() % (nMax - nMin + 1) + nMin); } int _tmain(int argc, _TCHAR* argv[]) { srand((unsigned)time(NULL)); char *pBuf = new char[ALLOC_GRANULARITY]; //缓存 //读取500个单词到vector vector<string> vecWord; //存放单词的vector //省略读取F:\\hp1.txt文件中的单词并存放在vector中的代码。。。。 //fill the buffer string str; char *p = pBuf; str = vecWord[RandomGen(0, nWordNum-1)]; //get a string from the vector randomly while ((p+str.length()) < pBuf + ALLOC_GRANULARITY) { //copy string into the buffer strcpy_s(p, str.length()+1, str.c_str()); //set pointer p to the end of the string p += str.length(); //把单词最后一个null符号用空格覆盖,然后指针前移,否则会导致pBuf指向的字符串始终是第一个得到的字符串 *p++ = ' '; str = vecWord[RandomGen(0, nWordNum-1)]; //get a string from the vector randomly } vecWord.clear(); //省略写文件的代码。。。。 delete pBuf; return 0; }
以上需要的地方有如下几点:
1. string::c_str()返回的const char*是包含null结束符的,所以在使用strcpy_s需要在第二个参数指定缓冲区大小的时候加上1,因为string::length()返回的长度是不包括null结束符的
2. 要把每个单词都保存进pBuf中,还需要把每个单词的null结束符先覆盖掉,否则从pBuf读取字符串的时候只会读取到第一个存入的单词。所以这两行代码很重要:
//set pointer p to the end of the string p += str.length(); //把单词最后一个null符号用空格覆盖,然后指针前移,否则会导致pBuf指向的字符串始终是第一个得到的字符串 *p++ = ' ';