关于c ++:如何摆脱Visual Studio中的“不安全”警告/错误(strcpy,sprintf,strdup)

How to get rid of “unsafe” warnings / errors in Visual Studio (strcpy, sprintf, strdup)

我试图摆脱一些编译器警告,这些警告说strcpy,sprintf等不安全。
我知道为什么它们不安全,但是我想不出一种以C ++风格修复代码的好方法。

这是一段代码摘录:

1
2
3
extList->names[i]=(char *)malloc(length*sizeof(char));
strcpy(extList->names[i],extName);                     // unsafe
// strncpy(extList->names[i],extName,length);          // also unsafe

这是消息:

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

在不知道要复制的内容的长度的情况下,我无法想到一种安全的方法来在C ++中复制数据。
我知道这里有strlen(),但这也不安全,因为它假设(可能是错误地)假定数据为空终止。

也:

1
2
// used to concatenate:
sprintf(extStr,"%s%s",platExtStr,glExtStr);

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

使用std :: string进行连接很容易,但是随后我需要以某种方式将数据获取到extStr中(而不是使用strcpy,lol)。 string :: c_str()函数返回一个指向不可修改数据的指针,因此我不能仅将extStr设置为等于它。 (而且我甚至不确定c_str()指针是否需要稍后调用它的delete?它是否使用" new"分配空间?)

关于这个东西有什么建议吗?
这是10,000行文件的一部分,该文件不是我的……所以我不太想用C ++方式重写它。


您实际上并不需要编译指示来禁用它们。

对于win32 / msvc,在ProjectProperties->配置属性-> C / C ++-> Preprocessor-> Preprocessor Definitions中,添加以下宏:

1
2
_CRT_SECURE_NO_DEPRECATE  
_CRT_NONSTDC_NO_DEPRECATE

或者,您可以在命令行参数(-D_CRT_SECURE_NO_DEPRECATE)中传递。您可能可以在某些* .cpp文件的开头#define它们。
另外,它们可能更多(请参阅crtdefs.h-看起来有很多...)。这些警告通常会告诉您可以使用哪些宏禁用它们-只需读取编译器输出即可。


这是这个问题的另一个答案。

1
2
3
4
5
6
7
8
9
10
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif

        strcpy(destination, source);

#ifdef _MSC_VER
#pragma warning(pop)
#endif


如果仅消除警告是您的目标,只需定义此_CRT_SECURE_NO_WARNINGS,它将禁止所有弃用警告。但这不能解决不安全的CRT功能的潜在问题。

如果您使用的Visual Studio版本> = 2005,并且想要以正确的方式解决这些警告,则最简单的方法是在项目中的#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1

无需任何其他代码更改,您就可以观察到大多数警告是自动修复的。通过定义此窗口,将为大多数不安全的CRT功能自动调用安全重载功能。静态数组的缓冲区大小是自动计算的。

尽管动态分配的缓冲区无法通过这种方式修复,但我们需要手动对其进行修复。请参阅此链接以获取更多详细信息。

以下是通过编程方式更正示例的一种方法

1
strcpy_s(extList->names[i], length, extName);

您确实知道要复制多少-您已为其分配空间!

当然,您愿意复制的空间不会超过分配的空间吗?

我更喜欢使用一种通过限制复制的项目数来明确避免缓冲区溢出的方法。当我还是C程序员的时候

1
2
3
4
dest = malloc(len);         // note: where did we get len?
if ( dest is null )  panic!  // note: malloc can fail
strncpy(dest, src, len);
dest[len-1] =0;

这有点混乱,并且已经指出使用strncpy()这个方法最初是为固定宽度字段而不是字符串设计的。但是它确实

我们可以提供诸如strdup()和strlcpy()之类的方法。

我的建议:

1)。您的目标不是抑制警告,而是使代码健壮。

2)。复制字符串时,需要确保以下事项:

  • 保护自己免受不良输入的影响,例如,未终止的字符串或过长的字符串。
  • 保护自己免受malloc失败的困扰,
  • 强烈希望复制计数的字符数而不是复制,直到我们看到null
  • 如果您声称要构建字符串,请绝对确保将其终止

如果您的环境中有strlcpy()可用,那么您可以使用它,否则为什么不编写自己的实用工具呢?然后,如果仅在该功能中存在警告,则说明您已经本地化了,然后就出现了问题。


在第一个示例中,您已经知道长度了。由于您没有分配length+1字节,因此我假定length包含空终止符。在这种情况下,只需std::copy字符串:std::copy(extName, extName + length, expList->names[i]);

在第二个示例中,假设源字符串以null终止,您可以计算目标字符串的长度,然后再次使用std::copy进行手动连接,或者可以使用std::stringstd::copyc_str的结果输入目标(再次假设您为其分配了足够的空间)。

c_str()不分配需要外部删除的内存。

最后请注意,sizeof(char)始终为1,因此在malloc中是多余的,尽管该字符中的位数可能不是8(请参见CHAR_BIT)。


如果不考虑可移植性,则可以使用" strcpy_s"。


我认为您应该尽可能替换所有的函数调用,以调用自己的实现。一个很好的例子是替换strcpy并在其中调用特定于编译器版本的strcpy的函数。然后可以轻松地修改您的实现以适合您选择的任何编译器,特别是如果您要添加或更改平台/编译器时。

例:

1
2
3
4
5
6
7
8
9
10
11
char* StringCopy(char* Destination, const char* Source, size_t DestinationSize)
{
#ifdef _MSC_VER
  return strcpy_s(Destination, Source, DestinationSize);
#else
  if(!(strlen(Source) >= DestinationSize))
     return strcpy(Destination, Source);
  else
     return 0x0;
#endif
}


根据消息中的建议,使用_CRT_SECURE_NO_WARNINGS禁用此警告。

在ProjectProperties->配置属性-> C / C ++->预处理程序->预处理程序定义中,添加以下宏:

_CRT_SECURE_NO_WARNINGS


如果仅针对Windows平台编译此代码,则最好使用这些功能的安全版本。 但是,如果要跨多个平台(Linux,Aix等)编译此代码,则可以使用_CRT_SECURE_NO_WARNINGS在Windows项目配置文件(例如.vcxproj)中禁用警告,或者可以使用类似以下的代码段 这是在.cpp文件中已调用这些功能的地方。

1
2
3
4
5
#if _OS_ == _OS__WINDOWS
//secure function call
#else
//already written code
#endif