5

So we know that the standard doesn't force pointer sizes to be equal. (here and here) (and not talking about function pointers)

I was wondering how in reality that can be an issue. We know that void * can hold anything, so if the pointer sizes are different, that would be the biggest size. Given that, assigning a void ** to a char ** means trouble.

My question is how dangerous would it be to assume void * and char * have the same size? Is there actually an architecture where this is not true?

Also, 16-bit dos is not what I want to hear! ;)

Community
  • 1
  • 1
Shahbaz
  • 46,337
  • 19
  • 116
  • 182

2 Answers2

9

void * and char * are guaranteed to have the same size.

void ** is not guaranteed to have the same size as char ** (but very likey on your implementation they will).

(C99, 6.2.5p27) "A pointer to void shall have the same representation and alignment requirements as a pointer to a character type [...] Pointers to other types need not have the same representation or alignment requirements."

ouah
  • 142,963
  • 15
  • 272
  • 331
  • @DavidCowden what is the question that is left unanswered for you? – ouah Jun 30 '12 at 16:40
  • @DavidCowden I didn't edit the question. And if you look at the time I also didn't edit the answer: your comment is after the 5 min edit time. I don't know what you mean. – ouah Jun 30 '12 at 16:43
  • I added the C99 paragraph to illustrate my two lines in the 5 min time frame. And this was done before your inital comment. – ouah Jun 30 '12 at 16:52
  • @DavidCowden, this in fact _does_ answer my question. Since `void *` and `char *` have the same size, an array of `void *` can be safely viewed as an array of `char *`, that is the `void **` can be used as `char **`. – Shahbaz Jun 30 '12 at 23:46
  • @Shahbaz YES. lol it does now. The first version of it didn't (= I don't understand the big uproar.. I was pointing out that the original version of this answer didn't actually discuss `char **` and `void **`. – dcow Jul 01 '12 at 00:34
  • @Shahbaz: an array of `void *` can't be safely viewed as an array of `char *` because it violates effective typing – Christoph Jul 01 '12 at 09:05
  • @Christoph, by array, I meant a continuous list of elements, like what you get with `malloc`. Not the type with `[]`. – Shahbaz Jul 01 '12 at 09:25
  • @Shahbaz: same problem - allocated memory has an effective type defined by the last store, and access via an expression of incompatible type is UB; any memory location has an effective type and may only be accessed through an expression of that type, a compatible one or `char`; this implies that expressions of incomatible type can't alias, and compilers are free to optimize on that assumption – Christoph Jul 01 '12 at 09:35
  • @Christoph, what if you cast it to `char **` and never access it through the `void **` again? – Shahbaz Jul 01 '12 at 09:59
  • @Shahbaz: that should work as expected; it's still UB, though – Christoph Jul 01 '12 at 10:06
  • @Christoph, thanks for the help. Could you point me to where in the standard this particular case is discussed? – Shahbaz Jul 01 '12 at 10:08
  • @Christoph, thanks. I guess I would have to allocate new memory and assign pointers one by one to make sure all conversions are done according to the standard. – Shahbaz Jul 01 '12 at 10:35
3

Assigning pointers of different object types to each other is allowed as long as no alignment requirements are violated: The assignment will involve an (implicit) type conversion, so it is as (un)problematic as assigning a float to an int - it works in most cases but is allowed to blow up when a meaningful conversion is impossible.

char * and void * have compatible alignment requirements per spec, so assigning a char ** to a void ** variable (and vice versa) is never problematic. They even have compatible representation, which means in principle, accessing a char * through an expression of type void * - eg by dereferening a void ** which actually points to a char * - will work as expected in most cases. Of course, the converse (accessing a void * by dereferencing a char **) holds true as well.

For example, the p conversion specifier for printf() expects a void * and passing in an arbitrary pointer type is undefined behaviour. However, in case of char *, it should work even on exotic architectures (eg with different pointer representations) as long as the implementation conforms to the C standard.

Where problems may arise is aliasing analysis: Due to the effective typing rules, a void ** and a char ** can't alias, and if the programmer breaks that promise, strange things may happen. One should realize that because of effective typing (aka strict aliasing), C is actually strongly typed - the type system is just very unsound (ie doesn't protect you from violating its invariants)...

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • Could you explain that list part a bit more? Why would aliasing them cause problem? Is that due to optimization? Would two `char **`s aliasing each other be ok, but this not? If so, how is that? – Shahbaz Jun 30 '12 at 23:48
  • @Shahbaz: if you have a function with signature `void foo(char **a, void **b)`, then the compiler is free to assume that `a != b` (*this is a gross simplicfication and actually a lie*); if both `a` and `b` had type `char **`, the compiler can't make that assumption, which limits optimization and led to the introduction of `restrict`; note that this has nothing to do with `a` and `b` being pointer-to-pointers - the same were true if they had type `int *` and `float *` – Christoph Jul 01 '12 at 09:02