我在代码审查中对 std::copy 行感到担忧。我对其运行了 clang sanitizer,确认了该问题。我进入调试器,发现副本并未像我预期的那样溢出到结构中的下一个字段,我不明白为什么会这样。
在下面的程序中,在 std::copy 中,8
旨在表示 8 个元素(long 类型);但是 的类型src.arr[0]
是pointer to long[4]
。因此,我预计8
会尝试复制 8 个 long[4] 项,从而溢出 后面的数组arr
;即overflowArray
字段。但在我的测试中,它没有这样做,并且在审查的程序中它也没有溢出。
下面的程序中,clang 报告以下行未定义行为,但目前给出了预期结果。一个重要的问题:此代码在不同的平台和/或编译器上是否会导致不正确的结果?
std::copy(src.arr[0], src.arr[0] + 8, dst.arr[0]);
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <cstring>
struct Astruct
{
long arr[2][4];
long overflowArray[8];
};
void printAstruct(Astruct str)
{
std::cout << "arr: ";
for( size_t i = 0; i < 2; ++i)
{
for( size_t j = 0; j < 4; ++j)
{
std::cout << str.arr[i][j] << " ";
}
}
std::cout << "; overflowArray: ";
for( size_t k = 0; k < 8; ++k)
{
std::cout << str.overflowArray[k] << " ";
}
std::cout << std::endl;
}
void fillArray(Astruct& );
int main()
{
Astruct src{};
Astruct dst{};
std::cout << "src size: " << sizeof(src.arr) << "; dst size:" << sizeof(src.overflowArray) << std::endl;
// SETUP
std::cout << "dst before copy" << std::endl;
printAstruct(dst);
long* srcStart = &src.arr[0][0];
memset(srcStart, 0xFF, sizeof(src.arr));
std::cout << "src:" << std::endl;
printAstruct(src);
// the Smell: src.arr[0] is type: array of 4 longs:
std::copy(src.arr[0], src.arr[0] + 8, dst.arr[0]); // <-- Undefined Behavior
std::cout << "\ndst after copy:" << std::endl;
printAstruct(dst);
}
输出:
src size: 32; dst size:32
dst before copy
arr: 0 0 0 0 0 0 0 0 ; overflowArray: 0 0 0 0 0 0 0 0
src:
arr: -1 -1 -1 -1 -1 -1 -1 -1 ; overflowArray: 0 0 0 0 0 0 0 0
dst after copy:
arr: -1 -1 -1 -1 -1 -1 -1 -1 ; overflowArray: 0 0 0 0 0 0 0 0 <-- I expected these 0's to be overwritten
看来我看到的是正确的,smell
正如 clang sanitizer 所确认的,但我不明白为什么没有发生溢出,因为8
应该将结束地址推进到 8long
秒之后。
用消毒剂冲洗并确认了气味:
runtime error: index 8 out of bounds for type 'long[4]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior
为什么 overflowArray 没有被覆盖?它可以在其他平台和/或编译器中被覆盖吗?
我把代码改成:
std::copy(&src.arr[0][0], &src.arr[0][0] + 8, &dst.arr[0][0]);
因为&src.arr[0][0]
是pointer to long
。但有人告诉我这太令人困惑了,而且原始地址和我更改的地址都相同。