2
votes

I've been reading some articles about void* type pointers and found this requirement from the Standard.

6.2.5.27:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.

I see that the Standard does not guarantee all pointer types have the same length, so the bottom line here is that a void* pointer has the same length and alignment rules as char*, right?

What I don't get is the footnote 39), which says

The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.

My questions are:

  1. What does it mean by "interchangeability"? Does it say the argument and the return values of a function void* Func(void*) can both be char*?

  2. If so, is it an implicit conversion made by the compiler?

  3. And what is it about the members of unions? I really don't get a grasp of the meaning of this. Can anyone give me a simple example?

2
Without going though the details, it means a pointer is a pointer is a pointer. In C any type pointer can be assigned to void* and back without a cast. That is the basis for the interchangeability.No implicit conversion takes place, a void* pointer is simply a pointer without a specific type. And, since type controls pointer arithmetic, you can't do pointer arithmetic on void* pointers. Just like you cannot dereference a void* pointer because the type information is missing -- it would be an incomplete type. So you have to assign or cast a void pointer before dreferencing. - David C. Rankin
@DavidC.Rankin Those are all true statements, but I don't think they're what the quoted passage is about. It's about void* and char* having the same representation. void* doesn't have to have the same representation or alignment as other pointer types. - John Kugelman
I take that to mean they both are the same size and share the same alignment requirements. (this also dovetails with the strict aliasing rule -type compatible and char* exception) I haven't done a critical interpretation of the standard section, just more a practical discussion of how that section doesn't hold any hidden gotchas. - David C. Rankin
I have generally regarded this as a the standard saying that these interchangeable types are “compatible” for the purposes of satisfying function call requirements but giving up on working it into the official language of the specification. For example, you could define void *foo(void *p) in one translation unit and declare char *foo(char *p) in another, and call it using the latter, and it would work because the types are interchangeable. But it is undefined according to the normative text of the C standard, aside from this passage in C 2018 6.2.5 28 about the same representation. - Eric Postpischil
The question cites the passage as 6.2.5.27, which I presume means clause 6.2.5 paragraph 27 (you should not use that format as it fails to distinguish clause 6.5 paragraph 1 from clause 6.5.1), but I do not find it at 6.2.5 27 in any official version of the C standard. In 1999, it was paragraph 26. In 2011, it was paragraph 28 (although I am looking at a draft for 2011, but I think it was the last one before release). - Eric Postpischil

2 Answers

3
votes

In C any data pointer can be passed to a function that expects a void * and a void * can be stored to any pointer type. There is an implicit conversion between void * and other pointer types. But this does not mean that this conversion is harmless. On some architectures where void * and int * have a different representation, converting from int * to void * and then back to int * is specified as producing the same pointer value, but the converse does not hold: converting a void * to int * and back to void * may produce a different value, especially if the void * was not obtained by converting an int *.

Interchangeability means that this implicit conversion does not change the representation of the pointer. the conversion can be operated both ways successfully: converting a character pointer to void * and back produces the same pointer and vice versa.

Here is an example:

#include <assert.h>
#include <stdio.h>
#include <string.h>

int main() {
    char *s = "abc";
    char *s1;
    void *p;
    void *p1;

    assert(sizeof(p) == sizeof(s));
    memcpy(&p, &s, sizeof(p));
    p1 = s;
    assert(p == p1);
    memcpy(&s1, &p1, sizeof(s1));
    assert(s == s1);
    return 0;
}

Note however that this does not imply that !memcmp(&p1, &s, sizeof(p1)) because pointers could have padding bits. Neither can you violate the strict aliasing rule by casting through a void *:

  • float f = 1.0; unsigned int i = *(int *)(void *)&f' incorrect.
  • float f = 1.0; unsigned int i; memcpy(&i, &f, sizeof(i)); correct if sizeof(int) == sizeof(float) but may produce a trap value.
-2
votes

A pointer is just an address in the memory. You can think the memory is continuous region of a byte, which is very large (e.g. on a 32 bit process it will be 4 GB but usually the process is not able to use the whole depend on the system).

That mean the value of a pointer is actually an integer represent the zero-based index of a byte in the memory (e.g. pointer with value 0 refer to the first byte in the memory but in really you will not be able to de-reference this address due to it is a null pointer).

When you de-reference a pointer what it does is reading/writing to that address. The size to read/write is depend on the type of pointer. If a pointer is int and its size on that system is 32 bits, which is 4 bytes; it will read/write 4 bytes starting at that address. What alignment means is how the value stored in the memory. Let say if the value stored in memory need to be 16-bytes alignment that means its starting address must be multiply with 16.

What I explain here is just a high-level of the pointer, which should be enough for getting started. In reality it have a lot of things related to it like memory protection, paging, etc.