Assigning pointers of different types of objects to each other is allowed until alignment requirements are violated: the assignment will include a (implicit) type conversion, so it is (un) problematic as assigning a float int - it works in most cases, but it is allowed to explode when meaningful transformation is impossible.
char * and void * have compatible alignment requirements for each specification, so assigning a variable to char ** a void ** (and vice versa) is never problematic. They even have a compatible view, which basically means that access to char * through an expression of type void * - for example, by dereferencing a void ** , which actually points to a char * , will work as expected in most cases. Of course, the converse is also true (access to a void * by dereferencing a char ** ).
For example, the conversion specifier p for printf() expects void * , and the transfer in an arbitrary pointer type is undefined. However, in the case of char * it should work even on exotic architectures (for example, with different representations of pointers) if the implementation complies with the C standard.
If problems arise, this is anti-aliasing analysis: due to efficient typing rules, a void ** and a char ** cannot be an alias, and if a programmer breaks this promise, strange things can happen. It should be understood that due to efficient typing (aka strict anti-aliasing), C is actually strongly typed - the type system is just very unreasonable (i.e. does not protect you from breaking its invariants) ...
source share