And*_*ent 6 c++ memcpy visual-c++
使用C数组作为函数参数的经典memcpy问题.正如下面所指出的,我的代码中有一个错误,但错误的代码在本地环境中工作!
我刚刚在移植作业中遇到这种奇怪的行为,我正在使用对象模拟Macintosh Picture操作码播放.我的DrawString对象在回放时绘制了垃圾,因为它显然无法复制字符串参数.以下是我写的测试用例 - 注意手动复制循环如何工作但memcpy失败.在Visual Studio调试器中进行跟踪显示memcpy使用垃圾来覆盖目标.
两个本地Str255阵列上的Memcpy工作正常.
当其中一个成为堆栈中对象的成员时,它就会失败(在其他测试中,当对象在堆上时它也会失败).
以下示例代码显示了在operator =中调用的memcpy.我在构造函数中失败后将其移动到那里,但没有区别.
typedef unsigned char Str255[257];
// snippet that works fine with two local vars
Str255 Blah("\004Blah");
Str255 dest;
memcpy(&dest, &Blah, sizeof(Str255)); // THIS WORKS - WHY HERE AND NOT IN THE OBJECT?
/*!
class to help test CanCopyStr255AsMember
*/
class HasMemberStr255 {
public:
HasMemberStr255()
{
mStr255[0] = 0;
}
HasMemberStr255(const Str255 s)
{
for (int i = 0; i<257; ++i)
{
mStr255[i] = s[i];
if (s[i]==0)
return;
}
}
/// fails
void operator=(const Str255 s) {
memcpy(&mStr255, &s, sizeof(Str255));
};
operator const Str255&() { return mStr255; }
private:
Str255 mStr255;
};
-
/*!
Test trivial copying technique to duplicate a string
Added this variant using an object because of an apparent Visual C++ bug.
*/
void TestMacTypes::CanCopyStr255AsMember()
{
Str255 initBlah("\004Blah");
HasMemberStr255 blahObj(initBlah);
// using the operator= which does a memcpy fails blahObj = initBlah;
const Str255& dest = blahObj; // invoke cast operator to get private back out
CPPUNIT_ASSERT( dest[0]=='\004' );
CPPUNIT_ASSERT( dest[1]=='B' );
CPPUNIT_ASSERT( dest[2]=='l' );
CPPUNIT_ASSERT( dest[3]=='a' );
CPPUNIT_ASSERT( dest[4]=='h' );
CPPUNIT_ASSERT( dest[5]=='\0' ); // trailing null
}
Run Code Online (Sandbox Code Playgroud)
这可能是为什么(在我看来)typedef数组类型不好的一个很好的例子.
与其他上下文不同,在函数声明中,数组类型的参数始终调整为等效的指针类型.当数组传递给函数时,它总是衰减成指向第一个元素的指针.
这两个片段是等效的:
typedef unsigned char Str[257];
Str src = "blah";
Str dst;
memcpy( &dst, &src, sizeof(Str) ); // unconventional
Run Code Online (Sandbox Code Playgroud)
unsigned char src[257] = "blah";
unsigned char dst[257];
memcpy(&dst, &src, sizeof(unsigned char[257])); // unconventional
Run Code Online (Sandbox Code Playgroud)
在后一种情况下&dst,&src它们都是类型,unsigned char (*)[257]但这些指针的值与指向每个数组的第一个元素的指针的值相同,如果直接传递到这样的话,这将是什么dst并且src会衰减memcpy.
memcpy(dst, src, sizeof(unsigned char[257])); // more usual
Run Code Online (Sandbox Code Playgroud)
memcpy接受void*参数,因此原始指针的类型无关紧要,只有它们的值.
由于参数声明的规则(任何或未指定大小的数组类型被调整为等效的指针类型),这些声明fn都是等效的:
typedef unsigned char Str[257];
void fn( Str dst, Str src );
Run Code Online (Sandbox Code Playgroud)
void fn( unsigned char dst[257], unsigned char src[257] );
Run Code Online (Sandbox Code Playgroud)
void fn( unsigned char dst[], unsigned char src[] );
Run Code Online (Sandbox Code Playgroud)
void fn( unsigned char* dst, unsigned char* src );
Run Code Online (Sandbox Code Playgroud)
看一下这段代码,更明显的是,memcpy在这种情况下传入的值是指向传递指针的指针,而不是指向实际unsigned char数组的指针.
// Incorrect
void fn( unsigned char* dst, unsigned char* src )
{
memcpy(&dst, &src, sizeof(unsigned char[257]));
}
Run Code Online (Sandbox Code Playgroud)
使用typedef,错误不是那么明显,但仍然存在.
// Still incorrect
typedef unsigned char Str[257];
void fn( Str dst, Str src )
{
memcpy(&dst, &src, sizeof(Str));
}
Run Code Online (Sandbox Code Playgroud)
你应该写memcpy(mStr255, s, sizeof(Str255));.没有'&'.Str255已经是一个指针.这是根据C++标准4.2:
可以将"数组NT"或"未知T的数组"的左值或右值转换为"指向T的指针"的右值.结果是指向数组的第一个元素的指针.
为什么它在某处工作?有两个不同的指针(for mStr255和&mStr255),它们有不同的类型 - unsigned char *和unsigned char (*)[257].数组的地址与数组中第一个元素的地址相同,但是当您将它作为参数传递给函数时,您将获得堆栈上变量的地址.通过打字,Str255你隐藏了差异.检查以下示例:
unsigned char Blah[10] = "\004Blah";
struct X
{
void f1( unsigned char(&a)[10] ) // first case (1)
{
void* x1 = &a; // pointer to array of unsigned char
void* x2 = a; // pointer to unsigned char due to implicit conversion array-to-pointer
}
void f2( unsigned char* a ) // second case (2)
{
void* x1 = &a; // pointer to variable 'a' which is on the stack
void* x2 = a; // pointer to unsigned char
}
unsigned char x[10];
};
int main( int argc, char ** argv )
{
X m;
m.f1( Blah ); // pass by reference
m.f2( Blah ); // implicit array-to-pointer conversion
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当你写作时void f( Str255 a ),它等于第二种情况.