4

前几天我在考虑这个问题,我很好奇这是否是一个坏主意……假设有一个结构包含指向字符串数组的指针。memcpy() 会在下面的示例中复制“名称”数组指针吗?编辑:在这个例子中,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));
}
4

5 回答 5

6

呈现的代码中有几个错误,我将首先讨论这些错误,然后是我的指针与数组的股票描述。

struct charMap
{ 
  unsigned int * name;   
  unsigned int id;       
};
typedef struct charMap CharMapT;

这声明了一个结构类型,其中包括指向 unsigned int 作为第一个成员(名称)和一个 int 作为第二个成员(id)的指针。在具有默认字节打包的 32 位系统上,这将是8 字节宽(32 位指针 = 4 字节,32 位有符号整数 = 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 项)。

然后我们将memcpy()24字节(或 36 字节)从list&in&list是不必要的)到新分配的内存。这将复制超过 3 个 CharMapT 结构,而我们分配的其他 21 个结构保持不变(超出它们的初始默认构造)。

注意:您正在将 a 初始化为const char *声明为 的字段unsigned int *,因此即使编译了基本数据类型也会有所不同。假设您修复了结构并将指针类型更改为const char *,则 const 数据段中某处的静态字符串常量的地址(“NAME”常量的地址)将分配给 structList[0] 中元素的指针变量.name、structList[2].name 和 structList[3].name 分别。

这不会复制指向的数据。它只会复制指针。如果您想要数据的副本,那么您必须对它们进行原始分配(malloc、new 等)。

更好的是,使用 an std::vector<CharMapT>、 use std::stringforCharMapT::name和 usestd::copy()来复制源代码(甚至直接赋值)。

我希望这能解释你在寻找什么。


指针与数组辱骂

永远不要将指针与数组混淆。指针是一个保存地址变量。就像变量保存整数值,或者变量保存字符类型一样,指针中保存的值是地址intchar

数组是不同的。它也是一个变量(显然),但它不能是左值,并且几乎每个通常使用它的地方都会发生转换。从概念上讲,转换会产生一个临时指针,该指针指向数组的数据类型,并保存第一个元素的地址。有时该概念不会发生(例如应用地址运算符)。

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.
于 2012-11-27T21:10:21.143 回答
4

它不会复制您指向的实际数据name。它将复制指针,您将有 2 个指针指向 2 个对象中的同一位置(对于 2 个数组中的每对对象)。

于 2012-11-27T20:39:16.897 回答
3

您在这里真正需要知道的是,这memcpy将为您提供一些原始副本。因此,您将拥有两个指向相同数据的具有相同值(即地址)的指针。

在旁注中,您已声明name为指向 的指针int,这在这里当然是错误的。它应该是一个const char*. 此外,由于这是 C++ 而不是 C,因此如果有一天成为复杂类型std::copy,它不会巧妙地破坏您的代码,因此您会得到更好的服务。charMap同样,在大多数情况下,更喜欢std::string而不是。const char*

于 2012-11-27T20:40:13.260 回答
0

调用时您的使用sizeof()是错误的newCharMapT您正在分配一个元素数组。您必须指定元素的数量,但您指定的是字节数。所以你需要解决这个问题:

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;
}
于 2012-11-27T21:05:01.517 回答
0

我想添加到@EdS。的答案:

如果你这样做,你的代码就是比 c 风格的 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,它的元素与列表的内存大小一样多。(如果列表是两倍,这可能是列表中元素数量的许多倍。)

此外,memcpy如果 list 也是一个向量(实际上是 ac 函数),则实际上没有必要。你只需做一个简单的赋值操作:

structList = list; // given that list is a vector.

这将复制 memcpy 之类的元素。

于 2012-11-27T21:05:56.083 回答