0

I am building a hash library, this library works with different structs and all those structs haves an unsigned type as first member, an example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct data {
    unsigned hash;
    void (*func)(struct data *);
};

struct another_data {unsigned hash; int value;};

static void *hash_insert(const char *text, size_t size)
{
    unsigned *hash;

    hash = malloc(size);
    // *hash = hash(text);
    *hash = (unsigned)strlen(text);
    return hash;
}

static void func(struct data *data)
{
    printf("%u\n", data->hash);
}

int main(void)
{
    struct data *data;

    data = hash_insert("Some text", sizeof *data);
    data->func = func;
    data->func(data);
    free(data);
    return 0;
}

Since the first member of the struct and the struct itself haves the same alignment requirements, is it valid to call malloc with a pointer of the type of the first member in order to reserve space for the entire struct?

unsigned *hash = malloc(size); /* where size is the size of the struct */

EDIT:

In this related question provided by @MohitJain:

struct Msg
{
   unsigned int a;
   unsigned int b;
};

...

uint32_t* buff = malloc(sizeof(Msg));

// Alias that buffer through message
Msg* msg = (Msg*)(buff);

The strict aliasing rule makes this setup illegal

But in my case I am returning a void * from the function, I can use this (returned) pointer inside main without alignment issues, is this assumption correct?

Community
  • 1
  • 1
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • What do you mean "call `malloc` with a pointer"? This function takes as input argument an integer (size), not a pointer. – barak manos May 16 '16 at 10:10
  • @barakmanos: I mean using `unsigned *data = malloc(sizeof struct data);` instead of `struct data *data = malloc(sizeof struct data);`, bearing in mind that the first member of the struct is an `unsigned` – David Ranieri May 16 '16 at 10:17
  • 3
    This code is fine. The only deficit is that it will probably confuse someone else trying to maintain it. – jxh May 16 '16 at 10:20
  • What do you mean with _valid_? Off the top of my head: Yes, it is valid, but why do you need to do that? – LPs May 16 '16 at 10:21
  • 1
    Oh, you mean the **return-value** of `malloc`... Well, you can store that value in any type of pointer you like. A pointer is still a pointer. The question is, whether or not it will be safe to dereference that pointer afterwards... And that depends on how you do it (for example, if you attempt to dereference `hash+1`, then it's really up to how your structure is defined). And BTW, the exact same answer could be given about `char *hash = malloc(size)`, **even though** the first member of your structure is not a `char`. – barak manos May 16 '16 at 10:24
  • "The first member of the struct and the struct itself have the same alignment requirements" -- but that is *not* true, in general, and it's not necessarily true in this case, either. – Steve Summit May 16 '16 at 10:29
  • It's probably a temporary placeholder, but `strlen` is a pretty poor hash function. – Steve Summit May 16 '16 at 10:31
  • @SteveSummit, of course, I don't use `strlen` as hash function :P – David Ranieri May 16 '16 at 10:35
  • @LPs, there are several `struct`s and they must be unknown for the library – David Ranieri May 16 '16 at 10:37

5 Answers5

2

malloc is guaranteed to return memory aligned for any type.

Therefore it will work regardless of the alignment requirement of the different subtypes.

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
2

The rules about effective type and pointer aliasing say that it is fine to convert pointers between a struct (or any other "aggregate") and a pointer of the same type as the first appearing member in the struct. And another rule says that structs are not allowed to have padding in the very beginning.

So it is fine as far as the C standard goes... which doesn't really say much of the quality of the program.

The code does not make much sense. You clearly want to use the whole struct, so why not return a pointer of the struct type? Don't complicate things for the sake of it. You should always avoid using void* when there is no need for it, to increase type safety.

Overall, all these things would sort themselves out in a multi-file project with proper program design. If you had a separate file with the hash type and all functions using that type, there would be no doubt of how to write the program.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thank you Lundin, my code is an example, as pointed out in the question I am building a hash library, this library maps **different** structs, I need to reserve space for those `struct`s in the library in order to deal with colisions, do you suggest another way to reserve space other than using a `void *` or a pointer of the type of the first member? – David Ranieri May 16 '16 at 11:22
  • @AlterMann Is there a reason why you can't use the struct type itself? Are you trying to do private encapsulation of the struct members or something? If so, then you should use "opaque pointers" (pointer to incomplete type), not this. – Lundin May 16 '16 at 11:27
  • That was my first try, but in this way I have to implement a lof of callbacks to check for colisions in my program, one for each struct, this approac allows me to use the same code, this is an xy question :) – David Ranieri May 16 '16 at 11:46
  • 1
    @AlterMann Then maybe create a sub-struct containing all callback functions. Like `typedef struct { int x; ... callback_t callbacks; } foo_t` and then `typedef struct { void (*on_enter) (void); (void(*on_exit)(void); .... } callback_t`. I recently had a very similar situation in one project (an engine of a simple, event-driven GUI) and solved that way. I came up with all kinds of various events as the project progressed. With this approach, all I had to do was to add/remove some function pointers. And then the application could use them or ignore them as it pleased. – Lundin May 16 '16 at 11:57
1

Since structure member are allocated in the order they are declared, using

unsigned *hash = malloc(size); /* where size is the size of the struct */

can work if your pourpose is just using the hash data. It fails if you want to apply pointer aritmetic on it, so in this case using

hash++

is an undefined behavior.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
1

Yes it is perfectly fine for first member type to point to the memory returned by malloc (Memory is aligned for any data type). What you do with this memory later may cause issues if you are not careful.

[Extracts from Joachim Pileborg's answer @ Maintaining an array of pointers in C that points to two related types with minor changes]

Having a structure inside other structures or a common data type in other structures is a common way of emulating inheritance in C. This common member should contain the minimal set of data common to all structures in the "inheritance" hierarchy, and it must always be the first member in the inheriting structures.


Possible issue with this scheme is:

This "inheritance" scheme will work on all modern PC-like systems and their compilers, and have done so for a long time, but there's no guarantee that it will work on all systems and all compilers (if you're planning on porting the code to some rare system with weird hardware and compiler you might want to look out, but the cases where the "inheritance" scheme will not work is very small, and most people will never come in contact with such a system in their entire lifetime.) But once you point the same memory with struct data *, you may fall victime of strict aliasing rule. So you need to be careful there. Further readL What is the strict aliasing rule?

Community
  • 1
  • 1
Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • 1
    `What you do with this memory later may cause issues if you are not careful`, can you elaborate this, please? – David Ranieri May 16 '16 at 10:42
  • 1
    I am not sure strict aliasing rule applies here. Chosen answer on that question is a bit of a mess, but [Ben Voigts answer](http://stackoverflow.com/a/7005988/694733) mentions situations when aliasing is allowed (effective type). – user694733 May 16 '16 at 11:12
-1
unsigned *hash = malloc(size);

This will create allocate an array of unsigned integers. Total number of integers allocated will be size/sizeof(int). hash here is pointer to int.

Since the first member of the struct and the struct itself haves the same alignment requirements, is it valid to call malloc with a pointer of the type of the first member in order to reserve space for the entire struct?

The point is, that hash here is a separate variable which has nothing to do with the hash inside the structure. (It is in a separate namespace, if you want to look it up)

You can then cast hash to the struct variable.

struct data *data;
unsigned *hash = malloc(size * sizeof(struct));
data = (struct data *) hash;

But, what is the point. You can just as well remove the unnecessary unsigned hash pointer and go with the traditional.

struct data *data;
data = malloc(size * sizeof(struct));
Rishikesh Raje
  • 8,556
  • 2
  • 16
  • 31