我担心代码审查中的 std::copy 行。我在上面喷了消毒剂,确认了气味。我进入调试器,发现副本没有像我预期的那样溢出到结构中的下一个字段,我不明白为什么会出现这种情况。
在下面的程序中,在 std::copy 中,
8
旨在表示 8 个元素(long 类型);但是,src.arr[0]
的类型是pointer to long[4]
。因此,我预计 8
会尝试复制 8 个 long[4] 项,从而溢出 arr
之后的数组;即 overflowArray
字段。但在我的测试中,它没有,并且在审查的程序中它也可以正常工作而不会溢出。
#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
应该将结束地址提前超过8个long
。
用消毒剂叮当响并确认气味:
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
。
但被告知这太混乱了,而且原始地址和我的更改中的地址都是相同的。
这一行:
memset(srcStart, 0xFF, sizeof(src.arr));
仅将
src.arr
设置为0xFF模式,src.overflow
保持为0。将其更改为:
memset(srcStart, 0xFF, sizeof(src));
您会看到整个
dst
被覆盖。