当visualstudio2010发布时,它将对警告C4789进行改进;允许它捕获更多缓冲区溢出的情况。这篇博文将介绍C4789警告的内容,以及如何解决警告。
编译源文件时,可能会收到警告:“warning C4789:destination of memory copy is too small。”
此消息表示编译器已检测到代码中可能存在缓冲区溢出。
例1
假设源文件a.cpp包含以下内容:
1: #包括 <内存.h>
2:
三: 内景 p[1];
4:
5: 无效 条形图(){
6: 内存集(&p[1],1, 大小 ( 内景 ));
7: }
在“Visual Studio 2008命令提示符”中,如果使用以下命令编译:
cl/c/O2 a.cpp公司
您将收到警告:
a、 cpp(6):警告C4789:内存副本的目标太小
对于上面的示例,编译器检测到变量“p”的缓冲区溢出p’已被分配为具有一个元素的数组。数组是零索引的,所以第6行的memset取 第二 数组的元素;这意味着我们实际上是在向数组外的内存写入数据,从而破坏内存!
在这种情况下,用户很可能希望memset第一个元素,因此要解决这个问题,memset将更改为
内存集(&p) [0] , 1, 大小 ( 内景 ));
实际上,很多缓冲区溢出不会像示例1那样明显,因此我将提供更多的示例来帮助您进行调查。
例2
假设源文件a.cpp包含以下内容:
1: 短的 G1期;
2:
三: 无效 福( 内景 *十)
4: {
5: *x=5;
6: }
7:
8: 无效 条形图(){
9: 福(( 内景 *)&G1);
10: }
从“Visual Studio 2010 命令提示符”,如果使用以下命令编译:
cl/c/O2 a.cpp公司
您将收到警告:
a、 cpp(9):警告C4789:内存副本的目标太小
在本例中,我们创建了si的变量“G1”ze short(只有两个字节),但我们已经获取了它的地址,并将其强制转换为’int*’以传递给’foo’foo’然后将4个字节写入“x”指向的内存位置。由于“G1”的长度只有2个字节,因此存储“*x=5”将写入超过“G1”的内容,从而导致缓冲区溢出。
关于这个例子,有几个重要的事情需要注意。只有在VisualStudio2010中进行了改进后,才会捕获此缓冲区溢出。此外,将“foo”内联到“bar”中也会捕获此警告。这意味着只有在启用优化时才会捕获缓冲区溢出。
为了修复示例2中的缓冲区溢出,我们将“G1”声明为int。如果这不是一个选项,我们可以创建一个新变量来传递给“foo”,并将该变量赋给“G1”(将int截断为短值):
内景 是的;
富(&y);
G1=y;
例3
假设源文件a.cpp包含以下内容:
1: 内景 G1期;
2: 内景 G2级;
三:
4: 无效 福( 内景 **十)
5: {
6: *x=&G2;
7: }
8:
9: 无效 条形图(){
10: 福(( 内景 *)&G1);
11: }
来自“VisualStudio2010 X64个 交叉工具命令提示符”,如果使用以下命令编译:
cl/c/O2 a.cpp公司
您将收到警告:
a、 cpp(10):警告C4789:内存副本的目标太小
这个示例与示例2完全相同,但有一个关键区别。我们把“int”转换成“int*”。在x86上,这是无害的强制转换(int和int*大小相同,4字节)。但是,在x64上,“int”是4字节,“int*”是8字节,因此在x64上运行此代码时,此代码不再正确。
您可能遇到C4789中警告不正确的情况。这可能是因为编译器检测到沿着代码路径的缓冲区溢出而永远不会触发。
例4
1: __国际64 G1期;
2: 内景 长度1=8;
三:
4: 无效 福( 烧焦 *x号, 内景 长度){
5: 如果 (长度>8){
6: x[8]=1;
7: }
8: }
9:
10: 无效 条形图(){
11: 福(( 烧焦 *)&G1,长度fg1);
12: }
在“Visual Studio 2010命令提示符”中,如果使用以下命令编译:
cl/c/O2 a.cpp公司
您将收到警告:
a、 cpp(11):警告C4789:内存副本的目标太小
在本例中,编译器认为“G1”可能是缓冲区溢出,因为“x[8]=1”的赋值超出了“G1”的大小。但是,只要“lengthOfG1”是“G1”的正确长度,“x[8]=1”就永远不会为“G1”触发,因此决不会发生缓冲区溢出。
对于某些误报,唯一的选择是禁用警告。但是,在这个特定的示例中,将“int lengthOfG1=8”更改为
常量int 长度1=8;
会解决问题的。
如果您已经证明该警告是误报,那么有几种不同的方法可以禁用该警告。
1. 禁用一个功能的警告(推荐)
2. 禁用所有功能的警告
禁用一个功能的警告
编译器允许您禁用特定函数的警告。这是通过
#布拉格马 警告 ( 使残废 : 4789 )
在函数之前
#布拉格马 警告 ( 违约 : 4789 )
在函数之后。这将禁用该函数(以及内联该函数的任何函数)的警告(在本例中为警告4789)。
例5
1: __国际64 G1期;
2: 内景 长度1=8;
三:
4: #布拉格马 警告 ( 使残废 : 4789 )
5: 无效 福( 烧焦 *x号, 内景 长度){
6: 如果 (长度>8){
7: x[8]=1;
8: }
9: }
10: #布拉格马 警告 ( 违约 : 4789 )
11:
12: 无效 条形图(){
13: 福(( 烧焦 *)&G1,长度fg1);
14: }
15:
16: 无效 bar1(){
17: 福(( 烧焦 *)&G1,9);
18: }
“foo”周围有#pragma,您将不会收到任何警告;如果没有它,您将收到警告:
a、 cpp(13):警告C4789:内存副本的目标太小
a、 cpp(17):警告C4789:内存副本的目标太小
您还可以选择为出现警告的函数之一禁用警告。在上面的例子中,我们可以把#pragma放在’bar’的周围,而不是’foo’,然后我们消除第13行的警告,但仍然在第17行收到警告。
禁用所有功能的警告
如果需要完全忽略警告4789,可以在命令行中指定/wd4789。
氯/碳/氧 /wd4789型 a、 cpp公司
不建议使用此选项,因为它会在代码中隐藏潜在的缓冲区溢出。