C++字符串处理strcpy_s注意事项

问题描述:

    之前公司开发用的VS2008,后面需要将项目升级使用VS2015,重新编译都是字符串处理函数的安全检查错误,类似于:

error C4996: ‘strcpy’: This function or variable may be unsafe. Consider using strcpy_s instead

    str*_s等字符串处理函数是具有更强"安全性"的CRT函数,为了安全考虑,那就替换吧;
    稍微了解了一下使用方法,对于第二个参数,有说是源字符串长度+1,也有说是目的字符串buff长度,稍微写demo测试了一下,发现两种方式都可行,并且感觉作为目的字符串buff长度更合理一些,知道了目的字符串buff长度就不用担心拷贝越界了(自以为是😂);
    但是测试的时候出问题,一测试就奔溃,示例代码如下:

#include <windows.h>
#include <string.h>
BYTE bszCmd[4] = { 0x01, 0x02, 0x03, 0x04 };
int main()
{
	int iCmdLen = 0;
	BYTE bszBuff[256] = { 0 };

	strcpy_s((char*)bszBuff, 256, "hello");
	iCmdLen += strlen("hello");

	memcpy(bszBuff + iCmdLen, bszCmd, sizeof(bszCmd));
	iCmdLen += sizeof(bszCmd);

	strcat_s((char*)bszBuff, 256 - iCmdLen, "world");
	iCmdLen += strlen("world");

    return 0;
}

代码逻辑层没改,就是只是替换了函数strcpy->strcpy_s、strcat->strcat_s


原因分析:

    经过调试发现,是strcat_s函数崩溃了,问题出在了strcpy_s上,跟strcpy_s函数的第二个参数有关系;
    strcpy_s函数拷贝的字符串长度是就是第二个参数的长度,不管有没有遇到‘\0’,调试界面如下:

在这里插入图片描述
    好吧,第二参数可以传目的字符串buff长度,但是遇到’\0’不会结束,会继续拷贝,以至于后面的内存值不可控,使用strcat_s找’\0’肯定越界;
    之前的代码没问题,是因为之前strcpy拷贝到’\0’就结束,后面内存块的值不会受影响,内存值还是’\0’,使用strcat没问题;
    说到底,还是自己代码不够严谨啊😰


解决方案:

    首先,str*_s系列函数使用的时候,第二个参数还是尽量使用源字符串长度+1(必须大于或等于这个值,否则会断言错误),尽管使用目的字符串buff长度也可以;
    另外,也要尽量避免字符串处理函数(strcpy等)和内存处理函数(memcpy等)混用;

哈哈,找那么多理由,说到底,就是代码逻辑不够严谨导致。。。

最后,附上strcpy_s源码

/*** 
*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);  // 验证字符串是否以null结尾
    _VALIDATE_POINTER_RESET_STRING(_SRC, _DEST, _SIZE);  // 记录字符串的原始信息,以便拷贝失败以后恢复
  
    p = _DEST; 
    available = _SIZE; 
    while ((*p++ = *_SRC++) != 0 && --available > 0) 
    { 
    } 
  
  	// 当源字符串_SRC不到\0字符,复制长度available减到0时,会引发断言
    if (available == 0) 
    { 
        _RESET_STRING(_DEST, _SIZE); 
        _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE); 
    } 
    _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1); // 完成在字符串最后加上null结束符的工作
    _RETURN_NO_ERROR; 
}

看完源码,我比较好奇,按道理拷贝到’\0’就结束了,为什么后面还会继续拷贝,_FILL_STRING到底做了什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_Zhi_Yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值