32

One of my friends pointed out from "Understanding and Using C Pointers - Richard Reese, O'Reilly publications" the second bullet point and I wasn't able to explain the first sentence from it. What am I missing?

Pointer to void

A pointer to void is a general-purpose pointer used to hold references to any data type. An example of a pointer to void is shown below:

void *pv;

It has two interesting properties:

  • A pointer to void will have the same representation and memory alignment as a pointer to char.
  • A pointer to void will never be equal to another pointer. However, two void pointers assigned a NULL value will be equal.

This is my code, not from the book and all pointers are having the same value and are equal.

#include <stdio.h>

int main()
{
  int a = 10; 
  int *p = &a; 
  void *p1 = (void*)&a;
  void *p2 = (void*)&a;

  printf("%p %p\n",p1,p2);
  printf("%p\n",p);
  
  if(p == p1) 
    printf("Equal\n");
  if(p1 == p2) 
    printf("Equal\n");  
}

Output:

 0x7ffe1fbecfec 0x7ffe1fbecfec
 0x7ffe1fbecfec
 Equal
 Equal
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Sreeraj Chundayil
  • 5,548
  • 3
  • 29
  • 68
  • 33
    Looks like the book is poorly written (or, at least, that part of it). What it *probably* means is that a `void` pointer will never be equal to a pointer that points to some other object. But I can only guess. – Adrian Mole Aug 01 '21 at 13:24
  • 8
    The claim sounds wrong. Maybe they have some valid meaning in mind, but it's not clear to me what it might be. A pointer to void is used as a universal pointer type (at least, for pointers to data, as opposed to function pointers). It can be equal to any other data pointer, depending on how those pointers have been defined. – Tom Karzes Aug 01 '21 at 13:25
  • 1
    I hope Richard Reese see this post and explain what he meant by it. – Sreeraj Chundayil Aug 01 '21 at 13:26
  • 1
    @InQusitive [this user](https://stackoverflow.com/users/10813237/richard-reese)? – Adrian Mole Aug 01 '21 at 13:28
  • 11
    Book is just wrong – Matt Timmermans Aug 01 '21 at 13:31
  • 9
    There is an [errata](https://www.oreilly.com/catalog/errata.csp?isbn=0636920028000) on this that says: *"A pointer to void will never be equal to another pointer." Should be: "A pointer to void will never be equal to another pointer **to void**". Reasoning: a pointer to void can be assigned the value of a non-void pointer*. Not sure if that makes it substantially better though. – GSerg Aug 01 '21 at 13:35
  • 2
    Isn't that book mostly famous for being entirely wrong on everything it says? – EOF Aug 01 '21 at 13:45
  • 2
    I looked at a random couple of pages on the book and I found an incorrect `printf("%d", sizeof x);` as well as it suggesting casting the result of `malloc`. It doesn't seem like this is an accurate book. – Paul Hankin Aug 01 '21 at 14:00
  • I'm not sure what they're talking, I'd get a new book. – LEF Aug 01 '21 at 14:01
  • 7
    @EOF You’re probably thinking of a different book, [*Mastering C Pointers* by Robert J. Traister](https://wozniak.ca/blog/2018/06/25/1/index.html). That one is indeed famous for being almost completely wrong, and written by somebody without any understanding of C or programming in general. – Konrad Rudolph Aug 01 '21 at 14:16
  • 2
    Take book outside. Apply gasoline. Throw match. – Martin James Aug 01 '21 at 17:50
  • @Paul Hankin: Both of those seem to work perfectly well with gcc. – jamesqf Aug 01 '21 at 21:50
  • 5
    @jamesqf In C just because it appears to work doesn't mean it's correct or that it's good code. There's undefined behavior and plenty of ways to write bad code that works. – eesiraed Aug 02 '21 at 05:31
  • @InQusitive He hasn't logged in for 2 years, so he probably won't see it. – Barmar Aug 02 '21 at 14:46
  • @Barmar: True, but who knows if it's really his account or some fake account :D – Sreeraj Chundayil Aug 02 '21 at 15:47
  • @GSerg I'm guessing the reasoning, perhaps it's not based on types at all but thinking of the "void" as the raw allocation result i.e. a "unique" address of some untyped data thus causing these 2+ allocations never being equal to each other neither as memory value nor as the content. Though that would apply only for the first allocation result and would be dependent on the allocating implementation. Nevertheless, it'd be still confusing and not appliable to most of the cases, so perhaps I'm just trying something that's not there. ㄟ(ツ)ㄏ – Peter Badida Aug 02 '21 at 17:44
  • 1
    @GSerg But you can also assign two different variables of type `void *` from the address of the same object, giving you two different void pointer variables that contain equal values. Even with the errata I can't see how the claim could be correct unless they literally mean it as "non-equal pointer values can't be equal" in which case it's technically true but uninformative (and **still** not a special property about void pointers rather than other pointers?). – Ben Aug 02 '21 at 22:46
  • 1
    *A pointer to void will never be equal to another pointer. However, two void pointers assigned a NULL value will be equal.* What if two void pointers are assigned to NULL? This statement says that they'll be unequal (because they're void) and equal (because they're NULL) at the same exact time. – Panzercrisis Aug 03 '21 at 13:34
  • @BessieTheCookie: I'm curious as to why you think either is incorrect. Of course "bad code" is most often a matter of opinion, but the only problem I see is a matter of style, not putting the argument of sizeof in parens. – jamesqf Aug 03 '21 at 16:18
  • @jamesqf `sizeof` is a `size_t`. `printf` with `%d` expects an `int`. So on most platforms you would be giving `printf` a 64-bit unsigned integer while it expects a 32-bit signed integer. Reasons why casting the result of malloc is bad is listed at https://stackoverflow.com/q/605845/9254539. – eesiraed Aug 03 '21 at 18:39

5 Answers5

55

TL/DR: the book is wrong.

What am I missing?

Nothing, as far as I can see. Even the erratum version presented in comments ...

A pointer to void will never be equal to another pointer to void.

... simply is not supported by the C language specification. To the extent that the author is relying on the language specification, the relevant text would be paragraph 6.5.9/6:

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

void is an object type, albeit an "incomplete" one. Pointers to void that are valid and non-null are pointers to objects, and they compare equal to each other under the conditions expressed by the specification. The usual way that such pointers are obtained is by converting an object pointer of a different (pointer) type to void *. The result of such a conversion still points to the same object that the original pointer did.

My best guess is that the book misinterprets the spec to indicate that pointers to void should not be interpreted as pointers to objects. Although there are special cases that apply only to pointers to void, that does not imply that general provisions applying to object pointers do not also apply to void pointers.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    Look at 6.3.2.3 Pointers. It talks about converting to another type and back, but never talks about what happens if you convert two pointers-to-X to two pointers-to-Y and *compare them as pointers to Y*, when there is no Y actually there. The comparison descriptions always mentions "and back again". There is no object of type void *there*. For `char*`, the pointers talk about it pointing at the first character in the byte representation; that is there. This may be an oversight in the standard, but it looks like it avoids allowing you to compare pointers *to the wrong type* being legal... – Yakk - Adam Nevraumont Aug 02 '21 at 16:34
  • 2
    First, @Yakk-AdamNevraumont, no C implementation of which I am aware so much as bats an eye at comparing two void pointers with each other for equality. Second, 6.3.2.3 has little to do with it. A comparison of two void pointers satisfies the third alternative in the "one of the following shall hold" constraint in 6.5.9/2 (which is where I suspect the book makes its key mistake). Then 6.5.9/5 says that if one of the pointers being compared is a pointer to `void` then the other is converted to the same for the comparison (a no-op in the special case where both are void pointers) [...] – John Bollinger Aug 02 '21 at 17:07
  • [...] That would be pointless if the resulting pair of pointers could not compare equal to each other. In that case, the spec would just say directly that the two pointers compare unequal. The only plausible interpretation of 6.5.9/6, then, is that such pointer conversions result in pointers to the same object, so that it is possible that for them to compare equal to each other. That type `void *` conveys no information about the types of those objects is immaterial. We evidently can (and C does) know from the pointer values whether the objects they point to are the same object. – John Bollinger Aug 02 '21 at 17:11
  • One of the interesting things about C is its support for weird architectures. Consider segmented mode on the 80286 - it's possible to have two pointers to the same address with completely different representations, because of the segment+offset nature of a pointer. Those pointers would probably not compare equal. – Mark Ransom Aug 03 '21 at 15:21
  • @MarkRansom, I acknowledge that it is possible in principle and may be observable in practice that two pointers to the same object have different representation. And the *really* interesting thing is that C requires such pairs of pointers to compare equal to each other, the difference in representation notwithstanding. – John Bollinger Aug 03 '21 at 17:42
14

C 2018 6.5.9 6 says:

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

So, suppose we have:

int a;
void *p0 = &a;
void *p1 = &a;

Then, if p0 and p1 “point to the same object”, p0 == p1 must evaluate as true. However, one might interpret the standard to mean that a void * does not point to anything while it is a void *; it just holds the information necessary to convert it back to its original type. But we can test this interpretation.

Consider the specification that two pointers compare equal if they point to an object and a subobject at its beginning. That means that given int a[1];, &a == &a[0] should evaluate as true. However, we cannot properly use &a == &a[0], because the constraints for == for pointers require the operands point to compatible types or that one or both is a void * (with qualifiers like const allowed). But a and a[0] neither have compatible types nor are void.

The only way for a fully defined situation to arise in which we are comparing pointers to this object and its subobject is for at least one of the pointers to have been converted either to void * or to a pointer to a character type (because these are given special treatment in conversions). We could interpret the standard to mean only the latter, but I judge the more reasonable interpretation to be that void * is included. The intent is that (void *) &a == (void *) &a[0] is to be interpreted as a comparison of a pointer to the object a to a pointer to the object a[0] even though those pointers are in the form void *. Thus, these two void * should compare as equal.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    *However, one might interpret the standard to mean that a `void *` does not point to anything while it is a `void *`.* IMO [the definition of `void`](https://port70.net/~nsz/c/c11/n1570.html#6.2.5p19) does not support that interpretation. "\[A\]n incomplete object type that cannot be completed" can still be addressed/pointed to. It just can't be dereferenced or accessed via a `void *`. – Andrew Henle Aug 01 '21 at 15:09
  • 3
    @AndrewHenle: I don't think you're actually disagreeing with this answer. The answer doesn't advocate the interpretation that you're quoting; on the contrary, the entire rest of the answer is devoted to showing how that interpretation wouldn't really make sense. – ruakh Aug 02 '21 at 04:08
7

The following section from this Draft C11 Standard completely refutes the claim made (even with the clarification mentioned in the 'errata', in the comment by GSerg).

6.3.2.3 Pointers

1     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.

Or, this section from the same draft Standard:

7.20.1.4 Integer types capable of holding object pointers

1    The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

      intptr_t

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    That doesn't really address the exact issue in question though, does it? This says the result of the conversion *back again* should compare equal to the original typed pointer, not that it should compare equal while still being `void*`. – GSerg Aug 01 '21 at 13:51
  • 4
    @GSerg I added the second excerpt, which is more specific to two values of `void*` type. – Adrian Mole Aug 01 '21 at 13:52
6

A pointer is just an address in memory. Any two pointers are equal if they're NULL or if they point to the same address. You can go on and on about how that can happen with the language of structures, unions and so on. But in the end, it's simply just algebra with memory locations.

nsayer
  • 16,925
  • 3
  • 33
  • 51
  • 2
    Some kinds of pointer math are undefined behaviour, so the reasoning of pointers just being integer addresses isn't always this simple in other cases. The C standard leaves the door open for segmentation and other ways to have multiple representations for the same actual address, so they don't have to compare equal if you derive them in different ways. (That usually involves undefined behaviour, so you wouldn't be able to safely deref such a pointer either.) Anyway, this answer is not wrong for this question, but be careful of over-simplifying. C isn't as simple as asm with a flat mem model. – Peter Cordes Aug 03 '21 at 00:55
  • @PeterCordes: To make things more interesting, in the language processed by clang's optimizer, converting pointers to integers and then comparing those integers can result in behavior that is consistent neither with the integers being equal, nor with them being unequal. – supercat Aug 03 '21 at 15:08
3
  • A pointer to void will never be equal to another pointer. However, two void pointers assigned a NULL value will be equal.

Since NULL is mentioned in that statement, I believe it is a mistype. The statement should be something like

  • A pointer to void will never be equal to NULL pointer. However, two void pointers assigned a NULL value will be equal.

That means any valid pointer to void is never equal to NULL pointer.

273K
  • 29,503
  • 10
  • 41
  • 64