3

I have the need to allocate memory for an array of pointers to structs. The documentation for malloc() informs that the return from this function returns a pointer to the first block of memory that was allocated. Well, in my case I called the function like this:

malloc(SIZE_TABLE*sizeof(struct structName*))

The problem being that, since malloc returns a pointer to this memory (containing pointers), it will thus return a 2-star pointer of type structName.

Instead of having an extra line to assign the 2-star pointer and then dereference it, can you dereference the call itself?

Instead of:

struct structName** temp = malloc(SIZE_TABLE*sizeof(struct structName*))

struct structName* temp2 = *(temp);

Could you do something like:

struct structName* temp2 = *( malloc(SIZE_TABLE*sizeof(struct structName*)) );

I have done little when it comes to 2-star pointers, so if there is a better way to approach this problem (or if I am simply miss-understanding it all), please let me know.

sherrellbc
  • 4,650
  • 9
  • 48
  • 77
  • 2
    Simple answer: No. You wind up with undefined data in `temp2` and lose the pointer to the allocated memory, ensuring it will leak. – Dark Falcon Jan 17 '14 at 14:14
  • 1
    Apart from the simple "no", what you're wanting to do doesn't make sense anyway. You're *allocating* memory for `SIZE_TABLE` *pointers*. Wanting the immediate pointer *value* in the `[0]` slot of the resulting vector will get you (a) an indeterminate pointer value, and (b) lose any reference the actual allocation outright since nowhere do you persist the *original* result address. – WhozCraig Jan 17 '14 at 14:20
  • There's another reason not to do this: malloc can *fail*, if so you'll end up dereferencing a NULL-pointer, which will crash. – kusma Jan 17 '14 at 14:20
  • Perhaps you are right.. This code is for the implementation of a hash table. What I was wanting to do was dynamically allocate the memory required for a list of pointers to structures that will be hashed into the table. The idea being that when I later had to add items into the table, I would not have to deal with a 2-star pointer, but rather just offset the *first* pointer in the array to get to the appropriate index. But now that I think about it more thoroughly, this would result in a memory leak right off. – sherrellbc Jan 17 '14 at 14:23
  • There are ways to implement a hash table with a single vector and single indirection, but it honestly isn't as trivial as using a pointer array as your proposing. You could preallocate k*N items for a table with a managed load factor of N, and use chained indices from items < N (the hash codes) to create the collision chains. It isn't as memory efficient, but rehashing on a table expansion is *trivial*, and nowhere is double indirection required. I venture to say it is likely you don't need something that elaborate, so I'd stick with your original code unless you're up for the challenge. – WhozCraig Jan 17 '14 at 14:40
  • Do you suggest any documentation on the subject? My approach was simply an array of pointers to structures that contain necessary data *and* pointers to the next struct in the chain. The code I proposed above is what I was using to dynamically allocate memory for the pointers that would point to the first structure in the chain. I did not want to try the open addressing method because it would require, potentially, longer hashing times if collisions are frequent - as opposed to O(1) for chaining (if adding to the front of the chain). – sherrellbc Jan 17 '14 at 14:48

4 Answers4

5

No you can not do that directly.

malloc returns void*, dereferencing a void* is undefined, and it will generate a compiler warning.
But you can cast it to different pointer type and then dereference.
The following may work:

struct structName* temp2 = *((struct structName**)malloc(SIZE_TABLE*sizeof(struct structName*)));

But this is a serious memory leak as the original return value of malloc is lost and is not good practice at all as you are not sure about the return value of malloc. If it is NULL dereferencing is not possible.

There is another problem in doing that. The value at the address returned by malloc is uninitialized, so dereferencing it after casting also lead to undefined behavior.

Always check for NULL before dereferencing any pointer. and
Always save the return value of malloc to free it later.

Dipto
  • 2,720
  • 2
  • 22
  • 42
  • So, the simple solution then it to first *cast* malloc and then dereference it.. the only problem being that you then cannot determine whether malloc failed to allocate the memory and returned a NULL pointer. Or does a casting of a NULL pointer fail ? – sherrellbc Jan 17 '14 at 14:16
  • I never have casted the the return of malloc because I always check whether it returned NULL afterwards to ensure memory was allocated. I was thinking in terms of the problem in the post that it could be resolved by casting the return first before dereferencing to avoid udnefined behavior. – sherrellbc Jan 17 '14 at 14:19
  • What would be the best approach to this problem then? Would it be to first assign, then dereference? – sherrellbc Jan 17 '14 at 14:20
  • I think the main question is, why are we explicitly dereferencing (instead of using array notation) in the first place? – tabstop Jan 17 '14 at 14:21
  • @sherrellbc, yes , check the return value, and assign, then dereference. – Dipto Jan 17 '14 at 14:21
  • 1
    @sherrellbc *what problem?* That you want to write one line of code rather than two? – WhozCraig Jan 17 '14 at 14:21
  • 1
    "But this is not good practice at all" It isn't just not good practice; its an outright memory leak. Nowhere is the original returned address from `malloc()` persisted *anywhere*. – WhozCraig Jan 17 '14 at 14:24
  • Your proposal introduces a memory leak, as the reference to the memory returned by malloc isn't saved an therefore lost. – alk Jan 17 '14 at 14:25
  • This was more an exercise in learning how to work with these pointers. It was simply an idea I had, but as it turns out it's a big problem. The memory leak issue I addressed in a comment under the original post. – sherrellbc Jan 17 '14 at 14:27
  • @alk, yes, you are right. I included that in my edit. Thanks. :) – Dipto Jan 17 '14 at 14:28
3

No you can't. This is because the return value of malloc is void *, and it is not known that to which type this void * get converted in this case. Dereferencing a void * invokes undefined behavior.

You will also get a warning in most compiler if you do so e.g. for GCC

warning: dereferencing ‘void *’ pointer [enabled by default]    

I would not suggest you to cast the return value of malloc and then dereference it. Better to stick with your first approach.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
2

As others have said, you can dereference it if you add a type cast. But you shouldn't. One reason already mentioned is checking for NULL. The other is:

  • you always have to save the pointer that malloc returns in order to be able to free the memory later.
bolov
  • 72,283
  • 15
  • 145
  • 224
  • @Dipto I'll sue you. Expect to hear from my lawyers soon. – bolov Jan 17 '14 at 14:34
  • Just out of curiosity, what would happen if you casted a NULL pointer? My conjecture is that it should not affect anything, since the check for NULL is simply a check of the *value* of the pointer, rather than its type, right? If true,what is all the fuss about casting the return from malloc? Isn't such casting implicitly done *anyway* if not explicitly written by the programmer? – sherrellbc Jan 17 '14 at 14:43
  • @sherrellbc The issue of casting the return from `malloc` is not related with the `NULL` value. Read the linked link why some say you should not cast the return type of `malloc`. I for one do cast it. The issue with NULL in you case is dereferencing it. You should check if the pointer in not `NULL` before dereferencing it. – bolov Jan 17 '14 at 15:02
1

You need to keep in mind that your malloc allocates SIZE_TABLE pointers to struct structName, but these struct structName are not allocated. Even worse: the buffer allocated by malloc is not initialized, so none of the pointers is initialized/valid.

What you need to do:

struct structName ** table = malloc(SIZE_TABLE*sizeof(struct structName*));
size_t i;
for(i = 0; i < SIZE_TABLE; ++i)
    table[i] = malloc(sizeof(struct structName));

What you propose:

struct structName * tableEntryAtIndex0 = malloc(SIZE_TABLE*sizeof(struct structName*));

This does not compile, you cannot dereference a void *.

Either of these two lines would compile:

// Either the way you wanted it
struct structName * tableEntryAtIndex0 = *((struct structName **)malloc(SIZE_TABLE*sizeof(struct structName*)));
// Or with an index to punctuate that a table was malloc-ed (in my opinion better to understand what happens)
struct structName * tableEntryAtIndex0 = ((struct structName **)malloc(SIZE_TABLE*sizeof(struct structName*)))[0];

This still has two problems: first you are losing the pointer to the overall table. So you cannot check it for NULL, you cannot access any other table entry and you cannot free the memory. Second the table was not initialized by malloc, so you are getting a garbage pointer. Now setting tableEntryAtIndex0 to malloc(sizeof(struct structName)) would not help, because this would just modify tableEntryAtIndex0 and not the entry in the table.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
  • Allocating the memory necessary for the actual structures was something I handled later on. What I did initially (just after allocated memory for the pointers, was do a memset() to zero to ensure all pointers are originally NULL. In this way, later I have another function that would allocate the necessary memory and set the pointers to the address returned by a call to malloc(). – sherrellbc Jan 17 '14 at 14:40
  • Casting the return of malloc prior to dereferencing it was something that I had mentioned as a comment in a post above this one. It seems like a lot of convolution for a simple problem, so I think I will rather have an extra line or two of code to accomplish this task. – sherrellbc Jan 17 '14 at 14:41
  • Only the 1st line of the last example leaks memory. The 2nd leads to a compiler warning about incompatible pointer types. – alk Jan 17 '14 at 14:55
  • @alk Compiles here, don't see a reason what could be wrong. What's the compiler's exact warning (which types)? You compiled only *one* of these lines, not both!? – Werner Henze Jan 17 '14 at 15:07
  • I missed the `[]` operator at the end of the line, as I didn't scroll. Sry. Everything was/is fine. – alk Jan 17 '14 at 16:48