0

I am using a library that contains this code (I have cut out irrelevant parts)

// Represents user-defined data
typedef char* aiUserData;

...

struct aiFile
{
    // Callback to write to a file
    aiFileReadProc ReadProc;

    // Callback to retrieve the current position of 
    //  the file cursor (ftell())
    aiFileTellProc TellProc;

    ...

    // User-defined, opaque data
    aiUserData UserData;
};

I need to use the last struct attribute UserData. It would be convenient to use a struct for the purpose, however as one can see in the first line UserData is declared type char *. I read somewhere that char * is similar to void *. Does that mean that I can do something like:

.UserData = &MyDataStruct;

Or is this not safe?

user10607
  • 3,011
  • 4
  • 24
  • 31

3 Answers3

2

The library is badly designed, it should be a void *.

You can legally convert any pointer to/from void *, and convert void * to/from char *, so it's safe.

When setting the pointer, you must cast to char *:

static struct mydata {
  float pi;
} data; /* This is what we want to store. */

struct aiFile whatever;
whatever.UserData = (char *) &data;

and later when you want to use the pointer, you must do the inverse cast:

struct mydata *d = (struct mydata *) whatever.UserData;

In practice you'd of course typedef your struct mydata too; I omitted it for brevity.

If the library had done the right thing and used void *, none of the casting would be necessary.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Oh so I have to cast it back before each usage? That's unexpected, I think you just saved me a lot of frustration right there. – user10607 Oct 21 '14 at 12:20
1

Yes, we can assume that's safe.

It is suggested to hold "user data", so we can assume that the underlying library isn't going to use it, and you can pass a pointer to whatever you like:

.UserData = (char *)&MyDataStruct;

It's entirely up to you to interpret it correctly at the other end, i.e., in this case as your data structure.

Ian
  • 1,221
  • 1
  • 18
  • 30
  • It seems that @2501 is suggesting to do `.UserData = (struct MyStruct *) MyDataStruct`. Am I misunderstanding something? – user10607 Oct 21 '14 at 12:07
  • I guess it depends how strict your compiler is on checking types. – Ian Oct 21 '14 at 12:10
  • @user10607 The cast is different when setting the pointer in the library's struct, and when *using* that pointer for your purposes, of course. – unwind Oct 21 '14 at 12:11
0

Character pointer is allowed to alias to any other pointer type.

If you cast your .UserData pointer to the correct type( typeof MyDataStruct ), you can use the object it points to.

You are also allowed to dereference a character pointer without casting as long as the pointer points to valid memory. What value the memory holds is another matter and you should use it correctly.

And a quote why is it allowed:

6.5. p7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object,

— a qualified version of a type compatible with the effective type of the object,

— a type that is the signed or unsigned type corresponding to the effective type of the object,

— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or

a character type.

And:

6.2.5. p28 A pointer to void shall have the same representation and alignment requirements as a pointer to a character type

6.3.2.3. p1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

Therefore a char pointer can store a pointer of any type, the same way as a void pointer.

Of course as you mention with your reference, none of this holds true for function pointers. You cannot mix them and other types.

2501
  • 25,460
  • 4
  • 47
  • 87
  • I read C99 spec, section 6.2.5, paragraph 27, its too long to post in a comment, but my concern is that it does not explicitly specify that a pointer to char is compatible with a pointer to struct. (Similar to the fact that function pointers are not always compatible with pointers to data). What do you think? – user10607 Oct 21 '14 at 12:04
  • But it can not store pointer to a function as indicated by the accepted answer to this question http://stackoverflow.com/questions/3941793/what-is-guaranteed-about-the-size-of-a-function-pointer right? I guess that's why the standart specifies that `An object shall ...`. A function is not an object so its pointer may be of a different size which void * or char * would not be able to hold. – user10607 Oct 21 '14 at 12:17
  • @user10607 That is something completely different. Why didn't you ask that in your question? If that is the problem you already have your answer. – 2501 Oct 21 '14 at 12:19
  • No that is not the problem, but I just wanted to understand what is going on. – user10607 Oct 21 '14 at 12:21
  • @user10607 Ok, I though only that was the issue. – 2501 Oct 21 '14 at 12:24