2

I want to understand the real need of having a void pointer, for example in the following code, i use casting to be able to use the same ptr in different way, so why is there really a void pointer if anything can be casted?

int main()
{
    int x = 0xAABBCCDD;
    int * y = &x;
    short * c = (short *)y;
    char * d = (char*)y;
    *c = 0;
    printf("x is %x\n",x);//aabb0000
    d +=2;
    *d = 0;
    printf("x is %x\n",x);//aa000000

    return 0;

}
Ahmad Anwar
  • 123
  • 1
  • 8
  • 4
    Void pointers do not have to be explicitly cast, and cannot be de-referenced. Also, the intent is built into the syntax. You can correctly assume, given `void * raw;`, that raw will likely be cast to a varying type. Whereas if you have a `char * raw;` it is not clear whether this is a byte array or a varying type. – 138 Sep 17 '19 at 19:13
  • 3
    @AhmadAnwar then how do you propose the function `void *memset(void *dest, int c, size_t count);` be defined? And your code is violating the [strict aliasing rule](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). – Weather Vane Sep 17 '19 at 19:30
  • 1
    Also see https://stackoverflow.com/questions/53932143/when-to-use-a-void-pointer-over-a-char-pointer – Arkku Sep 17 '19 at 19:38
  • The only thing i truly see as a reason is the strict aliasing rule mentioned above. – Ahmad Anwar Sep 17 '19 at 20:04

2 Answers2

6

Converting any pointer type to any other pointer type is not supported by base C (that is, C without any extensions or behavior not required by the C standard). The 2018 C standard says in clause 6.3.2.3, paragraph 7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer…

In that passage, we see two limitations:

  • If the pointer is not properly aligned, the conversion may fail in various ways. In your example, converting an int * to a short * is unlikely to fail since int typically has stricter alignment than short. However, the reverse conversion is not supported by base C. Say you define an array with short x[20]; or char x[20];. Then the array will be aligned as needed for a short or char, but not necessarily as needed for an int, in which case the behavior of (int *) x would not be defined by the C standard.
  • The value that results from the conversion mostly unspecified. This passage only guarantees that converting it back yields the original pointer (or something equivalent). It does not guarantee you can do anything useful with the pointer without converting it back—you cannot necessarily use a pointer converted from int * to access a short.

The standard does make some additional guarantees about certain pointer conversions. One of them is in the continuation of the passage above:

… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

So you can use a pointer converted from int * to access the individual bytes that represent an int, and you can do the same to access the bytes of any other object type. But that guarantee is made only for access the individual bytes with a character type, not with a short type.

From the above, we know that after the short * c = (short *)y; in your example, y does not necessarily point to any part of the x it originated from—the value resulting from the pointer conversion is not guaranteed to work as a short * at all. But, even if it does point to the place where x is, base C does not support using c to access those bytes, because 6.5 7 says:

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.

So the *c = 0; in your example is not supported by C for two reasons: c does not necessarily point to any part of x or to any valid address, and, even if it does, the behavior of modifying part of the int x using short type is not defined by the C standard. It might appear to work in your C implementation, and it might even be supported by your C implementation, but it is not strictly conforming C code.

The C standard provides the void * type for use when a specific type is inadequate. 6.3.2.3 1 makes a similar guarantee for pointers to void as it does for pointers to objects:

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.

void * is used with routines that must work with arbitrary object types, such as qsort. char * could serve this purpose, but it is better to have a separate type that clearly denotes no specific type is associated with it. For example, if the parameter to a function were char *p, the function could inadvertently use *p and get a character that it does not want. If the parameter is void *p, then the function must convert the pointer to a specific type before using it to access an object. Thus having a special type for “generic pointers” can help avoid errors as well as indicate intent to people reading the code.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Do you read "A pointer to any object type may be converted to a pointer to `void` ***and back again***" to require conversion to and from the same type such that "*the result shall compare equal to the original pointer.*". I would read it that way -- is that the wrong way to look at it? That would make it consistent with clause 6.3.2.3, paragraph 7. – David C. Rankin Sep 17 '19 at 22:05
  • @DavidC.Rankin: Yes, the conversion has to be back to the same type. For pointers, only compatible types (with qualifications like `const`) can be compared anyway, besides `void *` and a null pointer constant. – Eric Postpischil Sep 17 '19 at 23:33
2

Why void pointer if pointers can be casted into any type(in c)?

C does not specify that void* can be cast into a pointer of any type. A void * may be cast into a pointer to any object type. IOWs, a void * may be insufficient to completely store a function pointer.

need of having a void pointer

A void * is a universal pointer for object types. Setting aside pointers to const, volatile, etc. concerns, functions like malloc(), memset() provide universal ways to allocate and move/set data.

In more novel architectures, a int * and void * and others have different sizes and interpretations. void* is the common pointer type for objects, complete enough to store information to re-constitute the original pointer, regardless of object type pointed to.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256