0

I'm trying to do very basic examples to understand how void pointers work. Here's an example I've written for having a void* string and casting it to its "working" type and printing some aspects of it:

int main(int argc, char *argv[]) {

    // Create a void pointer which "acts like" a string
    void * string = "hello";

    // "Cast" the string so it's easier to work with
    char * string_cast = (char*) string;

    // Print the string and a character in it
    printf("The string is: %s\n", string_cast);
    printf("The third character is: %c\n", string_cast[2]);

    // How to now do something like:
    // (1) void pointer_to_string_obj = ?
    // (2) cast that pointer_to_string_obj to a normal string
    // (3) print the string like it would normally be done


}

Could someone please show an example of manually creating a string pointer of type *(char**) and why that type would need to be created in the first place (why not just a normal char*?). I apologize if my question is broad, but basically I'm trying to figure out various void pointer types and where I'm at now in my very beginner understanding, it's a bit confusing, and so seeing a few examples would be very helpful.

  • Do you already know double pointers that aren't `void`? – Joshua Sep 22 '19 at 01:29
  • @Joshua I've seen then and briefly used them, but no I wouldn't say I'm comfortable with anything beyond a single pointer/array. –  Sep 22 '19 at 01:30
  • 1
    Here's an example of double pointers used well. https://stackoverflow.com/questions/57846096/cancelling-elements-of-a-list-under-a-certain-value/57846240#57846240 ; but double void pointer is a different kettle of fish. – Joshua Sep 22 '19 at 01:34
  • Don't try to understand pointers in C with `void *`, as `void *` forbids expressely doing arithmetic with pointers, which is something required to understand C pointers. Better use different _pointed to_ pointers. But double pointers are fine, `void **` can be used, as the _pointed to_ thing is indeed a pointer (but trying to understand pointers using double pointers is not good way to learn also) Better use pointers to `int`. – Luis Colorado Sep 24 '19 at 13:08

1 Answers1

-1

So I thought up a kind of a good example of double void pointer (that is, void**). One way to cut down on double-free bugs is to always set pointers to NULL after freeing them.

We could do so like so (questionable style):

myprojectinclude.h:

/* must happen after any standard headers */
void freep(void **pointer);
#define free(p) error_call_freep_instead p /* so that free doesn't exist anymore */

freep.c:

#include <stdlib.h>
#include "myprojectinclude.h"

#undef free
void freep(void **p)
{
    if (p) {
        free(*p);
        *p = NULL;
    }
}

main.c:

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

int main()
{
     char *buffer = malloc(2048);
     size_t buffer_size = 2048;

     /* ... lots of code involving reading lines, etc. */

     freep(&buffer);

     /* buffer is guaranteed to be NULL here */
}

With this setup, double free is impossible. If we do

     freep(&buffer);
     freep(&buffer);

nothing goes wrong because buffer is NULL after the first call. (Note that passing NULL to free is safe; else we would add a NULL check like I had to do decades ago.)

Summary of portability debate in comments: This code assumes all data pointers are the same size and representation (mostly--the old 36 bit platform with same size and different bit layout for int* and char* had NULL look the same for both). There used to exist platforms for which this was not true. This code works on Unix, DOS, Windows, and most others.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 1
    This usage is technically invalid, though it often works: it accesses `buffer`, of type `char*`, via an lvalue of type `void*`, and those types are not compatible. The type `void**` can't be used as a "pointer to any pointer" type; it can only point at an actual `void*`. – aschepler Sep 22 '19 at 02:06
  • @aschepler: Are there any platforms left for which `char*` and `int*` are different sizes? – Joshua Sep 22 '19 at 02:11
  • I don't know. (and how do you define "left"?) But to write portable code, do you consider "all the platforms", or one Standard? – aschepler Sep 22 '19 at 15:45
  • @aschepler: The trouble with standards is there are so many to choose from. POSIX.1says this works because it dictates all pointer sizes are the same. – Joshua Sep 22 '19 at 18:16
  • Size alone is not enough. Does it also specify pointers use the same value representation? Not that it matters - we clearly have different views on what portable code means. – aschepler Sep 22 '19 at 18:48
  • You cannot pass a `char**` to a function expecting a `void**` **because the program won't compile** - it isn't valid standard C. – Lundin Jun 29 '23 at 14:54
  • @Lundin: It compiles with a warning on the compiler I learned on. "Suspicious pointer conversion." – Joshua Jun 29 '23 at 15:14
  • A warning is sufficient information to tell the programmer that they are writing invalid C. See [What must a C compiler do when it finds an error?](https://software.codidact.com/posts/277340) If you want _errors_ when writing invalid C, then on gcc-like compilers you have to actively specify `-std=c17 -pedantic-errors` – Lundin Jun 30 '23 at 06:43