ProgrammingC Developer, Systems Programmer

What are the rules for casting pointers of different types in C, what are the dangers of casting void* to other types and back, and how can one avoid memory-related errors when converting pointers?

Pass interviews with Hintsage AI assistant

Answer

Pointer casting is a common operation in C that allows for the use of generic interfaces, for example, working with memory through void* or implementing universal data structures. However, type casting of pointers comes with certain risks and follows strict standards.

  • void* in C holds an address but does not know the type of the content it points to. Any other pointer (such as int*, char*, struct mytype*) can be explicitly (or implicitly) cast to void* and back without loss of information (as long as no "narrowing" occurs).
  • However, if you cast a pointer of one type to another (not to void*), you must be sure that a compatible type's value really exists at that address.
  • Working with addresses allocated for one type but obtained through a "foreign" pointer is dangerous: it may lead to alignment issues, undefined behavior, or even runtime crashes.

Example

void process(void *data) { int *arr = (int*)data; // use arr as an array of int } int main() { double x = 10; process(&x); // DANGEROUS: casting double* to int*, UB }

Trick Question

"Can any pointer be safely cast to void* and back without loss?"

Many answer "yes"—after all, the C standard guarantees transforming any object pointer to void* and back without loss. But it's important to remember: if you cast a non-object pointer (such as a pointer to function) to void* or mix different architectures (pointer sizes differ for functions and data), you will encounter undefined behavior.

void foo() {} void *p = (void*)foo; // UB! function pointer cannot be cast this way

Examples of real errors due to ignorance of the topic


Story

In a project with a cross-platform data processing subsystem, a handler was used that cast a pointer to a structure to void*, then back to the original type. When transitioning to an architecture where int* and double* had different alignments, an attempt to cast void* to the incorrect type led to a "bus error" (crash).


Story

In an embedded project, a programmer implemented a ring buffer with a generic interface on void*, but forgot about strict alignment requirements (allocated memory for a char array, passed it as int*). On some platforms, the data became "incomprehensible to the hardware"—reading errors and unstable operation occurred.


Story

In a dynamic collection, addresses were stored as void*, but it was forgotten that a function pointer cannot be cast to void*. Trying to store and pass an event handler (callback) through such a field led to crashes on certain platforms, making it very difficult to catch the error.