Я думал об этом на днях, и мне любопытно, если это плохая идея …
Допустим, есть структура, которая содержит указатель на массив строк.
Будет ли 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;
Это объявляет тип структуры, который включает указатель на unsigned int в качестве первого члена (name) и int в качестве второго члена (id). В 32-битной системе с байтовой упаковкой по умолчанию это будет 8 байт широкий (32-битный указатель = 4 байта, 32-битный со знаком int = 4 байта). Если это 64-битная машина, то указатели будут иметь ширину 8 байт, а по-прежнему вероятную 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 предметов (36 пунктов при использовании 64-битных указателей).
Мы тогда memcpy()
24 байтов (или 36 байт) из list
( &
в &list
необязательно) вновь выделенной памяти. это скопирует 3 структуры CharMapT, оставляя остальные 21, которые мы выделили, нетронутыми (за пределами их первоначальной конструкции по умолчанию).
Примечание: вы инициализируете const char *
в поле, объявленное как unsigned int *
, так что, если это даже скомпилировано, основной тип данных будет другим. Предполагая, что вы исправили свою структуру и изменили тип указателя на const char *
адреса статических строковых констант (адреса констант «NAME») в вашем сегменте данных констант будут назначены переменным-указателям элементов в structList [0] .name, structList [2] .name и structList [3] .name соответственно.
Это НЕ будет копировать указанные данные в. это будет только копировать указатель ценности. Если вам нужны копии данных, вы должны их необработанно распределить (malloc, new, что угодно).
Еще лучше, используйте std::vector<CharMapT>
использовать std::string
за CharMapT::name
и использовать 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*
в большинстве ситуаций.
Ваше использование sizeof()
неправильно при звонке new
, Вы выделяете массив 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;
}
Я хотел бы добавить к ответу @ 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 также является вектором (это действительно функция c). Вы просто делаете простую операцию присваивания:
structList = list; // given that list is a vector.
Это скопирует элементы как memcpy.