关于安全字符串操作函数

    技术2022-05-20  66

     

          当我们在VC7.0及以上版本中使用非安全的字符串操作函数时,默认情况会提示类似如下的警告:

          warning C4996: 'wcsncpy': This function or variable may be unsafe. Consider using wcsncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

          建议我们使用安全字符串拷贝函数wcsncpy_s,也就是带“_s”后缀的一套字符串操作函数,使用这套函数的好处就是为了防止字符串操作时缓冲区溢出,缓冲区溢出带来的灾难就不说了。使用这套函数带来安全的同时,自然使用上会稍稍复杂一点,不过其实进行一个浅封装就可以很好的解决这个问题,如下:

    #pragma once #include <tchar.h> #include <assert.h> class KSafeStrOpFun { public: static bool SafeStrCpy(TCHAR *strDest, size_t sizeInTCHAR, const TCHAR *strSrc, size_t count = _TRUNCATE) { bool bRet = false; #if _MSC_VER <= 1200 // VC7.0以下的版本 bRet = (_tcsncpy(strDest, strSrc, min(sizeInTCHAR, count)) == 0); #else bRet = ((_tcsncpy_s(strDest, sizeInTCHAR, strSrc, count)) == 0); #endif if(!bRet) { #ifdef _DEBUG assert(false); #endif } return bRet; } }; #ifndef SAFE_TCHAR_STRCPY #define SAFE_TCHAR_STRCPY(DEST, SRC) KSafeStrOpFun::SafeStrCpy(DEST, sizeof(DEST)/sizeof(TCHAR), SRC, _TRUNCATE) #endif #ifndef SAFE_TCHAR_LEN_STRCPY #define SAFE_TCHAR_LEN_STRCPY(DEST, DEST_COUNT, SRC) KSafeStrOpFun::SafeStrCpy(DEST, DEST_COUNT, SRC, _TRUNCATE) #endif 

          可以这样使用:

    TCHAR szBuf[10] = {0}; SAFE_TCHAR_STRCPY(szBuf, TEXT("xx")); 

          是不是没有神马鸭梨(其他常用的几个函数,也可以使用类似的方法进行这样浅封装)。

          另外再看看那个_TRUNCATE宏,它被作为第四个参数传入了安全函数,在crtdefs.h中这个值被定义为-1,这样是看不出作用的,可以跟到这几个函数的源码中看看:

    /*** *tcsncpy_s.inl - general implementation of _tcsncpy_s * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * This file contains the general algorithm for strncpy_s and its variants. * ****/ _FUNC_PROLOGUE errno_t __cdecl _FUNC_NAME(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC, size_t _COUNT) { _CHAR *p; size_t available; if (_COUNT == 0 && _DEST == NULL && _SIZE == 0) { /* this case is allowed; nothing to do */ _RETURN_NO_ERROR; } /* validation section */ _VALIDATE_STRING(_DEST, _SIZE); if (_COUNT == 0) { /* notice that the source string pointer can be NULL in this case */ _RESET_STRING(_DEST, _SIZE); _RETURN_NO_ERROR; } _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE); p = _DEST; available = _SIZE; if (_COUNT == _TRUNCATE) { while ((*p++ = *_SRC++) != 0 && --available > 0) { } } else { _ASSERT_EXPR((!_CrtGetCheckCount() || _COUNT < _SIZE), L"Buffer is too small"); while ((*p++ = *_SRC++) != 0 && --available > 0 && --_COUNT > 0) { } if (_COUNT == 0) { *p = 0; } } if (available == 0) { if (_COUNT == _TRUNCATE) { _DEST[_SIZE - 1] = 0; _RETURN_TRUNCATE; } _RESET_STRING(_DEST, _SIZE); _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE); } _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1); _RETURN_NO_ERROR; } 

      仔细的跟一下代码会发现如果目的缓冲区不够存下所有的原字符串(如果是非安全的字符串拷贝函数,就会溢出了),如果最后一个参数不是_TRUNCATE,会抛一个_RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE)的异常,如果没catch这个异常,程序会崩溃,而如果是传的_TRUNCATE则函数会返回_RETURN_TRUNCATE,拷贝会不完全,程序不会崩溃。知道它的好处了吧,可以自己构造一些环境测试一下就更明了了。

     


    最新回复(0)