RLefevre RLefevre - 3 months ago 36
C++ Question

Is it alright to use memcpy() to copy a struct that contains a pointer?

I was thinking about this the other day and I am curious if this is a bad idea...
Lets say there is a structure that contains a pointer to a string array.
Would the memcpy() copy the 'name' array pointer in the below example?
Edit: The std is inaccessible in this example.

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));
}

Answer

There are several errors in the code presented, which I will talk about first, followed by my stock-diatribe of pointers vs. arrays.

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

This declares a structure type that includes a pointer to unsigned int as the first member (name) and an int as the second member (id). On a 32-bit system with default byte packing this will be 8 bytes wide (32-bit pointer = 4bytes, 32-bit signed int=4bytes). If this is a 64-bit machine the pointers will be 8 bytes wide, the int still-likely 32-bits wide, making the structure size 12 bytes.

Questionable Code

void ABC::Function ()
{
  CharMapT list[] = 
  {
    {"NAME1", 1},
    {"NAME2", 2}, 
    {"NAME3", 3}
  };

  structList = new CharMapT[sizeof(list)];
  memcpy(structList, &list, sizeof(list));
}

This allocates dynamic array of CharMapT structs. How many? More than you think. The sizeof(list) will return the byte-count of the list[] array. Since a CharMapT structure is 8 bytes wide (see above) this will 3 * 8, or 24 CharMapT items (36 items if using 64-bit pointers).

We then memcpy() 24 bytes (or 36 bytes) from list (the & in &list is unecessary) to the newly allocated memory. this will copy over 3 CharMapT structures, leaving the other 21 we allocated untouched (beyond their initial default construction).

Note: you're initializing a const char * to a field declared as unsigned int *, so if this even compiled the fundamental data type would be different. Assuming you fixed your structure and change the pointer type to const char *, the addresses of the static string constants (the addresses of the "NAME" constants) somewhere in your const data segment will be assigned to the pointer variables of the elements in structList[0].name, structList[2].name, and structList[3].name respectively.

This will NOT copy the data pointed to. it will only copy the pointer values. If you want copies of the data then you must raw-allocate them (malloc, new, whatever).

Better still, use an std::vector<CharMapT>, use std::string for CharMapT::name, and use std::copy() to replicate the source (or even direct-assignment).

I hope that explains what you were looking for.


Pointer vs. Array Diatribe

Never confuse a pointer with an array. A pointer is a variable that holds an address. Just like an int variable hold an integer value, or a char variable holds a character type, the value held in a pointer is an address

An array is different. It is also a variable (obviously), but it cannot be an l-value, and nearly every place it is typically used a conversion happens. Conceptually that conversion results in a temporary pointer that points to the data type of the array, and holds the address of the first element. There are times when that concept does not happen (such as the applying the address-of operator).

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

or this:

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)

but not this:

char ar[] = "Goodbye, World!";
++ar; //  error. nothing to increment.