-1

Suppose we have a struct "message_t":

message_t *msg;

If a function returns a void pointer can we simply assign the address to msg, or do we need to cast the pointer?:

void *data;
msg = data;

I've seen cases where "data" would be cast to message_t, however this doesn't seem entirely necessary, so in which situations would you do this?

Surely the pointer type (message_t) should be enough to tell the compiler how to dereference the pointer (i.e. how many bytes the first variable in the struct needs, etc).

Let me know if my question isn't clear.

  • 3
    You can't cast a pointer into a structure without dereferencing the pointer. You can cast it to a pointer to a structure though (`message_t*`). – Colonel Thirty Two May 04 '17 at 18:30
  • What does the cast actually do though? – user3163179 May 04 '17 at 18:47
  • 1
    You cannot just cast one type pointer to another. How did you obtain the `void *`? Read [ask] and provide a [mcve]. – too honest for this site May 04 '17 at 18:49
  • You can see a variant on this discussion in the context of dynamic memory allocation at [Do I cast the result of `malloc()`?](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) Suffice to say I'm not wholly in agreement with the argument, but the casts from `void *` are widely regarded as 'not necessary, possibly harmful' (I regard them more as 'not necessary, possibly beneficial'). – Jonathan Leffler May 04 '17 at 21:40

1 Answers1

3

You do not need to cast a void pointer, but you can if you want to, i.e., for clarity. The following simple sample illustrates this:

void* data = malloc(32);
char* msg  = data;
strcpy(msg, "Testing.");
Justin J.
  • 396
  • 2
  • 12
  • Ok this is starting to make sense! Could you maybe elaborate on why you would actually want to cast it? I think my confusion comes from the fact that it seems unnecessary. – user3163179 May 04 '17 at 19:02
  • Likewise, could we do: int a = 5; double *b = &a; Even thought me might get unexpected results. – user3163179 May 04 '17 at 19:05
  • @user3163179 Your comment example is `int*` to `double*` - not defined behavior in C. This good answer does a `void*` from `malloc()` to `char *` - that is OK. – chux - Reinstate Monica May 04 '17 at 19:14
  • @chux, why is it not defined behaviour? In theory shouldn't it work, despite the potentially undesired results? – user3163179 May 04 '17 at 19:18
  • @user3163179 It is not defined because C is portable to many platforms, old, popular and novel ones that take advantage of pointer differences. Example: An `int*` and `double*` may have different alignment requirements and may differ in size. What is missing is why your code needs this ability - which certainly can be better handle in other ways than casting - suggest adding your higher level goal to your post. – chux - Reinstate Monica May 04 '17 at 19:26
  • @chux, but using a cast would bypass this. Why not just make it automatic? – user3163179 May 04 '17 at 19:28
  • @chux Casting an `int *` to `double *` is **not necessarily undefined**. *If* the pointer is correctly aligned for both `int` and `double`, the C-standard guarantees that the pointer can be cast back and forth. You can't *access* the pointed-to object through an lvalue expression of the other type though (some exceptions apply). – EOF May 04 '17 at 19:42
  • @eof Agreed. Ref C11 conversions 6.3.2.3 goes into details. – chux - Reinstate Monica May 04 '17 at 19:53
  • 1
    @user3163179: I am one of those people who avoid casting as much as possible. In my experience, casting can hide bugs that become very hard to find. If you get a compiler warning or error that goes away with a cast, make sure you understand why before just casting. However, there are times when casting is helpful. For instance, a function that sends data out USB might take a byte pointer and size as parameters. You would need to cast what you want to send (if it is not already that type). Something like this: send_data((uint8_t*)&some_struct, sizeof(some_struct)); – Justin J. May 04 '17 at 20:48
  • @JustinJ. I agree with this entirely. I'm only asking to understand some code I've been given to study. I actually ran a few tests and determined that pointer casting is kind of unnecessary (with or without a cast, the results seem to be the same). The only difference is that without a cast, a compiler warning is outputted. So, it would appear that adding the cast is only to re-assure the compiler that you know what you are doing is potentially dangerous. Do you agree with this? ;) – user3163179 May 04 '17 at 21:08
  • @user3163179: Yes, a cast is basically telling the compiler “Leave me alone, I know what I’m doing”. The sample I posted compiles for me without warnings on 3 compilers I use regularly – if I remembered to include stdlib.h. If I “accidentally” comment it out, I get a warning: 'initializing': 'void *' differs in levels of indirection from 'int' on the malloc assignment (VS2015). If I had a cast there, I wouldn’t see the warning. This example is probably overly simplistic, but hopefully helps. – Justin J. May 04 '17 at 23:12
  • @JustinJ., Thanks you're really clearing this up for me. So is pointer casting only really for the pre-processor? I presume it doesn't affect the running of the code, etc.? If you were to compile two separate versions of a program - one with pointer casting and one without - would the assembly be identical (i.e. at machine level it makes absolutely no difference)? Sorry if i'm repeating myself, I just want to make sure I've got this under the belt. Thanks :) – user3163179 May 04 '17 at 23:40
  • @user3163179: For the simple example in my answer, the assembly was the same. I don't really know about others though. My advice is that if you are interested, write some code and take a look. With my Visual Studio compiler, I can look by going to Debug->Windows->Disassembly. I don't know what you use, but it should have something similar. – Justin J. May 04 '17 at 23:57
  • @JustinJ., I've just done a test as well and the assembly is the same for both. Just a side question though, I noticed this line: movq $655223333, -16(%rbp), where 655223333 is the decimal I assigned to one of the variables. I thought assembly used hexadecimal representations? – user3163179 May 04 '17 at 23:59
  • 1
    @user3163179: It depends. I've worked in a lot of assembly, and they are all different, but all could understand the different formats 10, Ah, 1010b, etc. Different compilers for the same processor could to it differently. – Justin J. May 05 '17 at 00:07