Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » memcpy with pointers

Credits go to anonymous and Evert for helping out!
This thread is locked; no one can reply to it. rss feed Print
memcpy with pointers
OnlineCop
Member #7,919
October 2006
avatar

Continuing some thoughts from here and here, I want to memcpy some data into my array.

object.h#SelectExpand
1typedef struct 2{ 3 int x, y; 4 float vel_x, vel_y; 5 float accel_x, accel_y; 6 7 char *name; 8 char description[32]; 9} s_object; 10 11typedef struct 12{ 13 unsigned int capacity; 14 unsigned int size; 15 s_object *array; 16} s_object_array;

With the above s_object, I have one char pointer to dynamically-allocated space for the name of the object. Then I have an array of (is it stack-allocated?) chars for the object's description.

I just want to make sure I correctly understand memcpy before I totally blow something up.

Let's say I have this:

EDIT: Fixed the sizeof() to return the size of the struct instead of the parameter (possibly a pointer).

object.c#SelectExpand
1void add_object_item(s_object_array *which_array, s_object *which_object) 2{ 3 ... // ensuring enough room to add it to the array 4 5 memcpy(&which_array->array[which_array->size], which_object, sizeof(s_object)); 6 which_array->size++; 7}

What I expect to happen is:

  • x, y, vel_x, vel_y, accel_x, accel_y will all be copied as expected.

  • The pointer to name will be copied (they both point to the same location), but I would want to allocate the array myself and then manually copy character-by-character the string over.

  • The description will ???

Technically, description is just a pointer, like name is. But since it's allocated on the stack (is this correct?), it wouldn't be pointing to the same stack address as the original would it? As soon as the other stack object goes out of scope, both the current and the original s_object objects will be invalid (at least, the description part of it).

Or does memcpy copy all 32 bytes? I don't know if I want to do this:

object.c#SelectExpand
1void add_object_item(s_object_array *which_array, s_object *which_object) 2{ 3 ... // ensuring enough room to add it to the array 4 5 memcpy(&which_array->array[which_array->size], which_object, sizeof(s_object)); 6 7 which_array->array[which_array->size].name = 8 malloc((strlen(which_object->name )+1) * sizeof(char)); 9 10 strcpy(which_array->array[which_array->size].name, which_object->name);
11 strcpy(which_array->array[which_array->size].description, which_object->description);
12 13 which_array->size++; 14}

or just

object.c#SelectExpand
1void add_object_item(s_object_array *which_array, s_object *which_object) 2{ 3 ... // ensuring enough room to add it to the array 4 5 memcpy(&which_array->array[which_array->size], which_object, sizeof(s_object)); 6 7 which_array->array[which_array->size].name = 8 malloc((strlen(which_object->name)+1) * sizeof(char)); 9 10 strcpy(which_array->array[which_array->size].name, which_object->name); 11 // which_array->array[which_array->size].description is already copied 12 13 which_array->size++; 14}

anonymous
Member #8025
November 2006

I had the impression that structs are assignable in C:

which_array->array[which_array->size] = which_object;

Do what it takes with the name pointer. The contents of the automatic array (description) should be automatically copied with the assignment (or with the memcpy).

OnlineCop
Member #7,919
October 2006
avatar

anonymous said:

I had the impression that structs are assignable in C:

Well, that's true. However:

example.c#SelectExpand
1void some_example(s_object_array *which_array) 2{ 3 unsigned int str_len;
4 s_object temp;
5 6 temp.x = temp.y = vel_x = vel_y = accel_x = accel_y = 0; 7 temp.name = (char*) malloc(20 * sizeof(char)); 8 strcpy(temp.name, "Temp object"); 9 strlen = sprintf(temp.description, "Temp #%d", some_number); 10
11 add_object_item(which_array, &temp);
12}

The temp object is destroyed when some_example() goes out of scope. Simply assigning the which_object pointer to the which_array->array array won't do it.

Quote:

The contents of the automatic array (description) should be automatically copied with the assignment (or with the memcpy).

Should be or will be? Do I have to worry about different platforms OR compilers (gcc/g++/MSVC/Borland C)?

anonymous
Member #8025
November 2006

This assignment does a shallow memberwise copy. It is basically human-readable and more error-proof way of calling memcpy on a single object.

OnlineCop said:

Should be or will be? Do I have to worry about different platforms OR compilers (gcc/g++/MSVC/Borland C)?

I would venture to say, will be (either way). I don't see what makes you even doubt: the array is part of the struct. When bytes get copied, the values of the bytes making up the char array are copied with them.

[Edit]

Wait, you are only copying sizeof(s_object*) worth of bytes?!

void add_object_item(s_object_array *which_array, s_object *which_object)
{
   ... // ensuring enough room to add it to the array

   memcpy(&which_array->array[which_array->size], which_object, sizeof(which_object));
   which_array->size++;
}

That won't copy the whole struct. First-hand example of assigning being less error-prone.

OnlineCop
Member #7,919
October 2006
avatar

I had mistyped the code in the OP. It's fixed.

I want to copy all 60 bytes of the s_object object from the which_object parameter into a new entry contained on the which_array array.

Instead of which_array->array containing pointers to newly-malloced instances, I wanted to allocate the room on the array itself so I could just step through them individually.

Since both name and description are char arrays, I was wondering whether memcpy would copy description in the same way that it copies name: ie, a shallow copy to the pointer only, and not a deep copy of the actual text.

Since name is allocated on the heap, while description is allocated on the stack, I didn't know whether it would shallow-copy the pointer, or whether the description gets deep-copied.

anonymous said:

Wait, you are only copying sizeof(s_object*) worth of bytes?!

That was a typo on my part. It was supposed to be a sizeof(s_object), not sizeof(s_object*). :-/

Evert
Member #794
November 2000
avatar

OnlineCop said:

Since name is allocated on the heap, while description is allocated on the stack, I didn't know whether it would shallow-copy the pointer, or whether the description gets deep-copied.

There should be no room for confusion. It copies the content of the struct. If the struct contains a fixed-size array, it copies the array (because it's part of the struct). If it contains a pointer, it copies the pointer. It will not duplicate whatever the pointer is pointing to, because that is not part of the struct (it would basically have to create a new pointer to a memory region that holds the copy, but then it wouldn't be copying the pointer).

Does that answer your question?

OnlineCop
Member #7,919
October 2006
avatar

Essentially. I had to write up a test program to make sure I was doing it correctly.

shallow_copy.c#SelectExpand
1#include <assert.h> 2#include <ctype.h> 3#include <math.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7 8 9typedef struct 10{ 11 int x, y; 12 float vel_x, vel_y; 13 float accel_x, accel_y; 14 15 char *name; 16 char description[32]; 17} s_object; 18 19 20typedef struct 21{ 22 unsigned int capacity; 23 unsigned int size; 24 s_object *array; 25} s_object_array; 26 27 28int add_object_item(s_object_array *, s_object *); 29 30 31int add_object_item(s_object_array *which_array, s_object *which_item) 32{ 33 unsigned int new_capacity, i; 34 assert(which_array && "which_array == NULL"); 35 assert(which_item && "which_item == NULL"); 36 37 if (which_array->size >= which_array->capacity) 38 { 39 new_capacity = which_array->capacity * 2; 40 41 which_array->array = realloc(which_array->array, sizeof(s_object) * new_capacity); 42 assert(which_array->array && "Could not realloc!"); 43 44 which_array->capacity = new_capacity; 45 } 46 47 for (i = 0; i < which_array->size; ++i) 48 printf("Array %d: \"%s\"\n", i, which_array->array[i].name); 49 50 memcpy(&which_array->array[which_array->size], which_item, sizeof(s_object)); 51 52 i = strlen(which_item->name); 53 which_array->array[which_array->size].name = malloc(i * sizeof(char)); 54 strcpy(which_array->array[which_array->size].name, which_item->name); 55 56 which_array->size++; 57 58 return 1; 59} 60 61 62 63int main() 64{ 65 unsigned int i; 66 s_object_array object_array; 67 s_object temp1, temp2; 68 69 printf("s_object size: %d\n", sizeof(s_object)); 70 printf("s_object_array size: %d\n", sizeof(s_object_array)); 71 72 object_array.array = malloc(sizeof(s_object)); 73 object_array.capacity = 1; 74 object_array.size = 0; 75 76 memset(&temp1, 0, sizeof(s_object)); 77 memset(&temp2, 0, sizeof(s_object)); 78 79 temp1.name = malloc(sizeof(char) * 50); 80 temp2.name = malloc(sizeof(char) * 50); 81 strcpy(temp1.name, "Blargh!"); 82 strcpy(temp1.description, "12345"); 83 84 printf("%d: name \"%s\", desc \"%s\"\n", 1, temp1.name, temp1.description); 85 printf("%d: name \"%s\", desc \"%s\"\n", 2, temp2.name, temp2.description); 86 87 add_object_item(&object_array, &temp1); 88 add_object_item(&object_array, &temp2); 89 90 // Changing this now should NOT affect the value within object_array 91 strcpy(temp1.name, "This value has been changed!"); 92 strcpy(temp1.description, "54321"); 93 94 for (i = 0; i < object_array.size; ++i) 95 { 96 printf("%d: name \"%s\", desc \"%s\"\n", i + 1, object_array.array[i].name, object_array.array[i].description); 97 free(object_array.array[i].name); 98 } 99 100 free(temp2.name); 101 free(temp1.name); 102 103 return 0; 104}

That's what I suspected all along, but didn't really want to write up 100+ lines of test code just to make sure that I was right. Oh well, it's another program to stick under my "~/programming/temp/things_i_learned/" pile :P

bamccaig
Member #7,536
July 2006
avatar

OnlineCop
Member #7,919
October 2006
avatar

Not if I have a really bad short-term memory ;D

Tobias Dammers
Member #2,604
August 2002
avatar

Evert said:

There should be no room for confusion. It copies the content of the struct. If the struct contains a fixed-size array, it copies the array (because it's part of the struct). If it contains a pointer, it copies the pointer. It will not duplicate whatever the pointer is pointing to, because that is not part of the struct (it would basically have to create a new pointer to a memory region that holds the copy, but then it wouldn't be copying the pointer).

Which is exactly why C++ provides copy constructors.
In C, I'd write up a clone_XXX() function for the struct in question, for any struct that requires deep copying.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

OnlineCop
Member #7,919
October 2006
avatar

Tobias, this is a clone_XXX() function. So within said function, I was trying to determine which of the two pointers/arrays were going to be deep copied, and which were going to be short.

My answer:

typedef struct
{
  int x, y;
  float vel_x, vel_y;
  float accel_x, accel_y;

  char *name;               // Shallow copy (pointer only)
  char description[32];     // Deep copy (all 32 bytes)
} s_object;

I'm so used to C++ that the usage of memcpy() is rather new to me :P

Peter Wang
Member #23
April 2000

The array is in the same contiguous block of memory and is also shallow copied. If you have an array of pointers only the pointer addresses would be copied.

OnlineCop
Member #7,919
October 2006
avatar

Peter: When you say "the array"... are you talking about char *name; or char description[32]; ?

Peter Wang
Member #23
April 2000

Only one of them is an array ;)

Evert
Member #794
November 2000
avatar

OnlineCop said:

I was trying to determine which of the two pointers/arrays were going to be deep copied, and which were going to be short.

I don't quite understand the distinction you make between "deep" and "short" copying here - and I'm not talking about what those terms mean. I just find it confusing to use those terms when talking about what happens when you copy a struct.
Again, that's because what happens is really simple: the content of the struct is copied because you copy the raw bytes that make up the struct. If you have an array in the struct, then the array gets copied. If you have a pointer, the pointer gets copied. Nothing else is touched.
Think of it this way: to memcpy() the struct is just a raw block of data. To decide whether it should duplicate a pointer or not it would need to interpret some of this data. Since it doesn't know anything about your struct, it doesn't know how to do that. So it doesn't.

Ron Novy
Member #6,982
March 2006
avatar

In C, this structure...

typedef struct
{
  int x;
  float y;
  char *name;
  char description[32];
} demo_struct;

... would look like this in memory (32-bit code).

Byte        | Description
00 01 02 03 = int
04 05 06 07 = float
08 09 0a 0b = 32-bit pointer to some memory

0c 0d 0e 0f 10 11 12 13 
14 15 16 17 18 19 1a 1b 
1c 1d 1e 1f 20 21 22 23 
24 25 26 27 28 29 2a 2b = 32 chars of description[32]

Since memcpy only copies a string of bytes it copies the pointer and not what the pointer points to... C is just too stupid to know what your pointing it at. To make a unique copy of that string you could always use strdup like this...

#SelectExpand
1typedef struct 2{ 3 int x; 4 float y; 5 char *name; 6 char description[32]; 7} demo_struct; 8 9demo_struct *demo_struct_copy(demo_struct *x) 10{ 11 demo_struct *ret = malloc(sizeof(demo_struct)); 12 if (ret) { 13 memcpy(ret, x, sizeof(demo_struct)); 14 ret->name = strdup(x->name); 15 } 16 return ret; 17}

And that's it...

----
Oh... Bieber! I thought everyone was chanting Beaver... Now it doesn't make any sense at all. :-/

GullRaDriel
Member #3,861
September 2003
avatar

Hell, that was kind of hard...

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Go to: