可以使用memcpy()来复制包含指针的结构吗
Is it alright to use memcpy() to copy a struct that contains a pointer?
前几天我在想这个问题,我很好奇这是不是个坏主意。。。假设有一个结构包含一个指向字符串数组的指针。memcpy()会复制下面示例中的"name"数组指针吗?编辑:在本例中,std是不可访问的。
struct charMap
{
unsigned char * name;
unsigned char id;
};
typedef struct charMap CharMapT;
class ABC
{
public:
ABC(){}
void Function();
CharMapT* structList;
}
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
给出的代码中有几个错误,我将首先讨论这些错误,然后是我对指针与数组的总结。
struct charMap
{
unsigned int * name;
unsigned int id;
};
typedef struct charMap CharMapT;
这声明了一个结构类型,其中包括一个指向无符号int的指针作为第一个成员(name),以及一个指向第二个成员(id)的int。在具有默认字节封装的32位系统上,其宽度为8字节(32位指针=4字节,32位带符号int=4字节)。如果这是一台64位机器,指针将为8字节宽,int仍然可能为32位宽,使结构大小为12字节。
可疑代码
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
这将分配CharMapT结构的动态数组。有多少比你想象的要多。sizeof(list)
将返回list[]
数组的字节计数。由于CharMapT结构是8个字节宽(见上文),这将是3*8,或24个CharMapT项(如果使用64位指针,则为36项)。
然后,我们从list
(&list
中的&
是不必要的)到新分配的存储器的memcpy()
24字节(或36字节)。这将复制3个以上的CharMapT结构,使我们分配的其他21个结构保持不变(超出了它们最初的默认构造)。
注意:您正在将const char *
初始化为一个声明为unsigned int *
的字段,因此,如果这是编译的,那么基本数据类型将有所不同。假设您修复了结构并将指针类型更改为const char *
,则const数据段中某个位置的静态字符串常量的地址("NAME"常量的地址)将分别分配给structList[0].NAME、structList[2].NAME和structList[3].NAME中元素的指针变量。
这不会将指向的数据复制到。它将只复制指针值。如果您想要数据的副本,那么您必须对其进行原始分配(malloc、new等等)。
更好的是,使用std::vector<CharMapT>
,对CharMapT::name
使用std::string
,并使用std::copy()
复制源(甚至直接分配)。
我希望这能解释你在寻找什么。
指针与阵列对角线
切勿将指针与数组混淆。指针是变量,保存地址。就像int
变量保存整数值,或者char
变量保存字符类型一样,指针中保存的值是地址
数组不同。它也是一个变量(很明显),但它不可能是一个l值,而且几乎每个通常使用它的地方都会发生转换。从概念上讲,转换会产生一个临时指针,指向数组的数据类型,并保存第一个元素的地址。有时,这个概念不会发生(例如应用运算符的地址)。
void foo(const char * p)
{
}
char ar[] = "Hello, World!";
foo(ar); // passes 'ar', converted to `char*`, into foo.
// the parameter p in foo will *hold* this address
或者这个:
char ar[] = "Goodbye, World!";
const char *p = ar; // ok. p now holds the address of first element in ar
++p; // ok. address in `p` changed to address (ar+1)
但不是这个:
char ar[] = "Goodbye, World!";
++ar; // error. nothing to increment.
它不会复制name
指向的实际数据。它将复制指针,并且在2个对象中有2个指向同一位置的指针(对于2个数组中的每对对象)。
这里您真正需要知道的是,memcpy
将为您提供原始文件的逐位副本。因此,您将拥有两个具有相同值(即地址)的指针,它们引用相同的数据。
附带说明一下,您已经将name
声明为指向int
的指针,这在这里当然是错误的。它应该是const char*
。此外,由于这是C++而不是C,所以最好使用std::copy
之类的东西,如果charMap
有朝一日成为复杂类型,它不会巧妙地破坏代码。同样,在大多数情况下,更喜欢std::string
而不是const char*
。
调用new
时,您对sizeof()
的使用是错误的。您正在分配一个CharMapT
元素数组。您必须指定元素的数量,但您指定的是字节计数。所以你需要解决这个问题:
structList = new CharMapT[sizeof(list) / sizeof(CharMapT)];
固定后,memcpy()
的结果将是structList
将包含list[]
所包含的原始数据的精确副本。这意味着structList[N].name
指针将包含与list[N].name
指针相同的值,因此它们都将指向字符串值的相同物理内存。
如果你想对字符串值进行深度复制,你必须分别分配它们,例如:
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
int num = sizeof(list) / sizeof(CharMapT);
structList = new CharMapT[num];
for (int i = 0; i < num; ++i)
{
int len = strlen(list[i].name);
structList[i].name = new char[len+1];
strcpy(structList[i].name, list[i].name);
structList[i].name[len] = 0;
structList[i].id = list[i].id;
}
...
for (int i = 0; i < num; ++i)
delete[] structList[i].name;
delete[] structList;
}
我想添加到@EdSs的答案:
如果你这样做的话,你的代码比c风格的c++代码要多得多:
#include<string>
#include<vector>
struct CharMap
{
CharMap(const std::string& name, unsigned char id); // only needed if you don't use -std=c++11
std::string name;
unsigned char id;
};
CharMap::CharMap(const std::string& name, unsigned char id):
name(name),
id(id)
{}
class ABC
{
public:
ABC(); // or ABC() = default; if you use -std=c++11
void Function();
private:
std::vector<CharMap> structList;
}
ABC::ABC(){} // not needed with -std=c++11
void ABC::Function ()
{
// This works with -std=c++11:
//structList =
//{
// {"NAME1", 1},
// {"NAME2", 2},
// {"NAME3", 3}
//};
// without c++11:
structList = std::vector<CharMap>(3);
structList[0] = CharMap("NAME1",1); // don't worry about copies, we have RVO (check wikipedia or SO)
structList[1] = CharMap("NAME2",2);
structList[2] = CharMap("NAME2",3);
}
为什么不使用std::vector
制作阵列?你可以这样做:
#include<vector>
std::vector<CharMapT> structList(list.size());
更安全的是,避免使用指针可以减少由于错误使用sizeof
运算符而导致内存泄漏或出现错误的几率。
我想你并不是真的想要structList,它的元素数量与列表的内存大小一样多。(如果列表是双倍的,这可能是列表中元素数量的数倍。)
此外,若列表也是一个向量(实际上是一个c函数),则memcpy
实际上是不必要的。你只需要做一个简单的分配操作:
structList = list; // given that list is a vector.
这将复制memcpy等元素。
- 如果基类包含双指针成员,则派生类的构造函数
- 包含矢量指针的结构的内存释放问题
- 指向包含对齐 C 结构C++类的 C 指针的对齐问题
- 包含指向其他结构的指针向量的结构
- 删除包含包含动态对象的 STL 容器的智能指针
- C++两个对象,其中包含指向同一数组不同部分的指针
- 编译包含指向模板函数的指针的初始值设定项列表时,gcc 出错,但 clang 不出错
- 为什么 Mac 上不需要包含智能指针?
- 指针数据类型变量如何包含对象?
- cppyy 继承包含智能指针的类
- 为什么 Wild 指针包含零地址而不是 garabge 地址?
- 为包含原始指针的对象C++智能指针
- 遍历包含 c++ 中指针的向量
- 如何在C++中重新实现包含指针的 STL 容器的类的迭代器
- C++对包含新指针的向量的分配
- 为什么此指针包含存储在堆上的地址
- 为什么指针包含一些垃圾
- 指向结构的指针包含指针
- 指针包含什么类型的数据
- 为什么变量指针包含相同数据类型的地址?