This question is a continuation of Malloc call crashing, but works elsewhere
I tried the following program and I found it working (i.e. not crashing - and this was mentioned in the above mentioned link too). I May be lucky to have it working but I'm looking for a reasonable explanation from the SO experts on why this is working?!
Here are some basic understanding on allocation of memory
using malloc()
w.r.t structures
and pointers
malloc(sizeof(struct a) * n)
allocatesn
number of typestruct a
elements. And, this memory location can be stored and accessed using apointer-to-type-"struct a"
. Basically astruct a *
.malloc(sizeof(struct a *) * n)
allocatesn
number of typestruct a *
elements. Each element can then point to elements of typestruct a
. Basicallymalloc(sizeof(struct a *) * n)
allocates anarray(n-elements)-of-pointers-to-type-"struct a"
. And, the allocated memory location can be stored and accessed using apointer-to-(pointer-to-"struct a")
. Basically astruct a **
.
So when we create an array(n-elements)-of-pointers-to-type-"struct a"
, is it
- valid to assign that to
struct a *
instead ofstruct a **
? - valid to access/de-reference the allocated
array(n-elements)-of-pointers-to-type-"struct a"
usingpointer-to-"struct a"
?
data * array = NULL;
if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
The code snippet is as follows:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
typedef struct {
int value1;
int value2;
}data;
int n = 1000;
int i;
int val=0;
data * array = NULL;
if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
printf("allocation successful\n");
for (i=0 ; i<n ; i++) {
array[i].value1 = val++;
array[i].value2 = val++;
}
for (i=0 ; i<n ; i++) {
printf("%3d %3d %3d\n", i, array[i].value1, array[i].value2);
}
free(array);
printf("freeing successful\n");
return 0;
}
EDIT: OK say if I do the following by mistake
data * array = NULL;
if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {
Is there a way to capture (during compile-time using any GCC
flags) these kind of unintended programming typo's which could work at times and might blow out anytime! I compiled this using -Wall
and found no warnings!
This
array = (data *)malloc(sizeof(data *) * n)
allocates asizeof(data*)
(pointer) to structdata
, if you want to do that, you need a yourarray
to be adata** array
.In your case you want your pointer to point to
sizeof(data)
, a structure in memory, not to another pointer. That would require adata**
(pointer to pointer).There seems to be a fundamental misunderstanding.
No, that's just what one usually does use it as after such a call.
malloc(size)
allocates a memory region ofsize
bytes. What you do with that region is entirely up to you. The only thing that matters is that you don't overstep the limits of the allocated memory. Assuming 4 bytefloat
andint
and 8 bytedouble
, after a successfulmalloc(100*sizeof(float));
, you can use the first 120 of the 400 bytes as an array of 15double
s, the next 120 as an array of 30float
s, then place an array of 20char
s right behind that and fill up the remaining 140 bytes with 35int
s if you wish. That's perfectly harmless defined behaviour.malloc
returns avoid*
, which can be implicitly cast to a pointer of any type, sois perfectly fine, it might just not be the amount of memory you wanted. In this case it very likely is, because pointers tend to have the same size regardless of what they're pointing to.
More likely to give you the wrong amount of memory is
as you had it. If you use the allocated piece of memory as an array of
n
elements of typedata
, there are three possibilitiessizeof(data) < sizeof(data*)
. Then your only problem is that you're wasting some space.sizeof(data) == sizeof(data*)
. Everything's fine, no space wasted, as if you had no typo at all.sizeof(data) > sizeof(data*)
. Then you'll access memory you shouldn't have accessed when touching later array elements, which is undefined behaviour. Depending on various things, that could consistently work as if your code was correct, immediately crash with a segfault or anything in between (technically it could behave in a manner that cannot meaningfully be placed between those two, but that would be unusual).If you intentionally do that, knowing point 1. or 2. applies, it's bad practice, but not an error. If you do it unintentionally, it is an error regardless of which point applies, harmless but hard to find while 1. or 2. applies, harmful but normally easier to detect in case of 3.
In your examples.
data
was 4 resp. 8 bytes (probably), which on a 64-bit system puts them into 1. resp. 2. with high probability, on a 32-bit system into 2 resp. 3.The recommended way to avoid such errors is to
No.
sizeof(struct a*)
is the size of a pointer.sizeof(struct a)
is the size of the entire struct.Well, technically speaking, it is valid to assign like that, but it is wrong (UB) to dereference such pointer. You don't want to do this.
No, undefined behavior.