0

I saw some code as following:

struct Hello
{
   int age;
   int time;
   int data[1];
};

struct Element
{
   int a;
   int b;
};

struct Element element_[5] = (/* Initiate the array */);
struct Hello* hello = (struct Hello*)malloc(sizeof(Element)*5);
struct Element * element_p = NULL;

element_p = (struct Element *)hello->data;

for(int i = 0; i<5; i++)
{
   memcpy(element_p, &element_[i], sizeof(struct Element));
}

for this line: element_p = (struct Element *)hello->data;, why we need to cast hello->data to (struct Element *)? And does this mean the operation on the pointer of element_p will does the same to the hello->data? and does this line will fill the address of each element of struct Element element_[5] to each element of hello->data?

  • 1
    First of all, that code won’t compile (`:` should be `;` and `;` should be `;` unless your compiler accepts `;`, which it doesn’t), and second, it looks more like C than C++ (and if it really is C++, it’s terrible). –  Dec 05 '12 at 20:05
  • I don’t judge anything. I’m just saying the code does not compile, and that is a fact, not an opinion. And it was unclear whether it was C or C++. –  Dec 05 '12 at 20:12
  • I modified the title and keywords of this post –  Dec 05 '12 at 20:14
  • @ratzip ok. Next time please take into account that C and C++ are **totally different languages**. – Luchian Grigore Dec 05 '12 at 20:14
  • 1
    That is called a [struct hack](http://c2.com/cgi/wiki?StructHack), there is an SO discussion about it [here](http://stackoverflow.com/questions/3711233/is-the-struct-hack-technically-undefined-behavior). You are not allocating anything in your example, but you can find examples how that is done in the first link. – Joe Dec 05 '12 at 20:20

2 Answers2

8

This is horrible horrible C++. The cast is needed because hello->data is actually an int[], so not directly convertible to an Element*.

Moreover, it's undefined behavior, since hello isn't initialized and yet you dereference it (via ->data).

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • why need this line?element_p = (struct Element *)hello->data;? does this mean we can use element_p to operate hello->data? –  Dec 05 '12 at 20:17
  • @ratzip you don't need that line, because that line is illegal. `hello->data` is illegal since `hello` wasn't initialized. If it was, then `p` would point to the memory location that `hello->data` points to. Modifying `*p` would modify `*hello->data`. – Luchian Grigore Dec 05 '12 at 20:21
  • ok, how about hello is initiated maybe because I missed that –  Dec 05 '12 at 20:26
  • I am not the one write these code, I saw it and trying to understand it –  Dec 05 '12 at 20:29
  • @ratzip I explained what happens when it's initialized. – Luchian Grigore Dec 05 '12 at 20:32
0

element_p must be a pointer to struct Element but hello->data is int data[1], so you need the cast. Suppose you initialized hello you "force" element_p to be a pointer to an area of memory where a single integer is, since in fact hello->data will point to the first and only integer "contained" in the array hello->data[]. Since struct Element "contains" 2 integer, element_p->a should access to hello->data[0], but element_p->b would try to accesso to hello->data[1] which is out of the bound of the array.

The for loop will copy data of the size of the struct Element (very likely 2*sizeof(int)), taken from each initialazed struct element_, into the are pointed by element_p that, as we've said and because of a missing part (see hack below), have room for just 1 int. Since element_p stays the same, you will overwrite that memory and only the last "struct" copied will survive.

As you showed to us, it looks like a good recipe for a crash. Anyway if the hack Joe talks in comment about is in place, element_p will point to the first int of several other "ints". Since ints are nothing but memory, they could be enough to accomodate struct Element structs you could access with element_p[0] the first, element_p[1] the second and so on. Likely something like

  hello = malloc(sizeof (*hello) + sizeof (struct Element) * 6);

or alike. Now, element_p = (struct Element *)hello->data can be used.

Anyway the for loop continues to write over the same area, element_p needs to be advanced by 1 (1 whole struct Element) in order to fill each time a different area (element_p+i shoudl go).

ShinTakezou
  • 9,432
  • 1
  • 29
  • 39
  • `data` should be declared as `char[1]` and the size argument to malloc should subtract 1. – Joe Dec 05 '12 at 20:50
  • ok, thanks, "since in fact hello->data will point to the first and only integer "contained" in the array hello->data[]." I do not understand it, what you mean? –  Dec 05 '12 at 20:52
  • @Joe why data should be char[1]? what is the reason/ –  Dec 05 '12 at 20:53
  • `char` is the only type that is guaranteed to be 1, so you could simply subtract 1 when allocating storage. You could also just subtract `sizeof(int)`. – Joe Dec 05 '12 at 20:57
  • some extra bytes allocated, no harm... – ShinTakezou Dec 06 '12 at 11:23