5

GCC gives me folowing warning:

note: expected 'const void **' but argument is of type 'const struct auth **

Is there any case, where it could cause problems?

Bigger snippet is

struct auth *current;
gl_list_iterator_next(&it, &current, NULL);

Function just stores in current some void * pointer.

Kache
  • 15,647
  • 12
  • 51
  • 79
KAction
  • 1,977
  • 15
  • 31

2 Answers2

7

The error message is clear enough: you are passing a struct auth ** where a void ** was accepted. There is no implicit conversion between these types as a void* may not have the same size and alignment as other pointer types.

The solution is to use an intermediate void*:

void *current_void;
struct auth *current;

gl_list_iterator_next(&it, &current_void, NULL);
current = current_void;

EDIT: to address the comments below, here's an example of why this is necessary. Suppose you're on a platform where sizeof(struct auth*) == sizeof(short) == 2, while sizeof(void*) == sizeof(long) == 4; that's allowed by the C standard and platforms with varying pointer sizes actually exist. Then the OP's code would be similar to doing

short current;
long *p = (long *)(&current);  // cast added, similar to casting to void**

// now call a function that does writes to *p, as in
*p = 0xDEADBEEF;               // undefined behavior!

However, this program too can be made to work by introducing an intermediate long (although the result may only be meaningful when the long's value is small enough to store in a short).

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • how is using an intermediate `void*` different from just casting it, assuming he isn't using the original `*current` for anything else? – Kache Sep 27 '12 at 12:30
  • If the alignment of `void*` could be different, how is that issue mitigated when you assign the pointer back to your `struct auth*` type? In other words if the function returns a `void*` to memory that is of a different alignment, and you simply assigned that pointer back to a `struct auth*`, shouldn't there be some type of compensation for the lack of alignment? Or is it simply the case that the alignment for `void*` is always a superset of any other pointer type's alignment requirements, so any other pointer will be compatible with `void*` alignment? – Jason Sep 27 '12 at 12:41
  • 1
    @Jason: when you say `current = current_void`, the compiler inserts a conversion from `void*` to `struct auth*` if necessary. Such a conversion is guaranteed to exist for all data pointer types (but not for function pointers, IIRC), because `void*` was designed to be a universal pointer type. – Fred Foo Sep 27 '12 at 12:46
  • 2
    @Kache: the difference is that the intermediate `void*` serves as a location where a value of type `void*` can be stored through indirection of a `void**`. Suppose that a `struct auth*` would be smaller than `void*` on some platform (which may happen), then the OP's code + a cast might write past the end of `&current`. It's as if you'd cast a `short*` to a `long*` and then store a `long` through indirection. – Fred Foo Sep 27 '12 at 12:50
  • It is very intresting. May I ask for example, under which OS, processor and other conditions, `sizeof(void *) != sizeof(foo *)`? – KAction Sep 27 '12 at 18:44
  • @illusionoflife: mostly word-addressed machines and those with segmented memory models. Those are rare, but the C standard still caters to them. See also http://stackoverflow.com/q/6751749/166749 and questions linked there. – Fred Foo Sep 27 '12 at 18:52
  • Whoa, didn't know that pointer size mismatch could happen. Not only that, you'd have to know that using the "larger" temporary container is safe b/c of the guaranteed data pointer type conversion. – Kache Sep 28 '12 at 03:11
0

Hm... I think constructs like const void * doesn't makes much sense.

Because if user wants to access data under void * he needs casting from void, and this action bypasses compiler type checks and consequently - constantness.

Consider this example:

#include <stdio.h>
#include <stdlib.h>

int main () {

  int i = 6;
  int * pi = &i;

  const void * pcv = pi;
  const int  * pci = pi;

  // casting avoids type checker, so constantness is irrelevant here
  *(int *)pcv = 7;

  // here we don't need casting, and thus compiler is able to guard read-only data
  *pci = 7;

  return 0;
}

So conclusion is that we need either void pointer Or to ensure constantness of data, but not both.

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
  • You are not right. `void *` can be implicityle morphed to `foo *`, but `const void *` can not. – KAction Sep 28 '12 at 07:46
  • My point was that `const` is very useful for getting compile-time error if somebody tries to modify read-only data. But this purpose is bypassed with construct `const void *` - because you can't modify data under `void*` without casting it to some concrete pointer. If you do so - type checker is bypassed as well as constantness. In the end user is able to modify read-only data without getting compile-time error. This was my point why `const void *` is bad thing. – Agnius Vasiliauskas Sep 28 '12 at 09:07
  • Void * is perfectly good. I do not need cast to use it, I use assignment. – KAction Sep 28 '12 at 12:16