0

I have a question about coding styles when using void pointers.

This code is modified from code geeks

#include<stdio.h>
int main()
{
    int a[2] = {1, 2};
    void *ptr = &a;
    for(int i=0; i<2; i++) {
        printf("%d\n", *(int *)ptr+i);
    }
    return 0;
}

I am using a generic circular buffer in my project. This buffer has a void pointer to a static buffer which is passed in during initialization. I want to traverse and print the data in the circular buffer. I was unsure how to print the contents until I found the code above.

I have two questions:

  1. Is this an appropriate well accepted way to traverse my data? Will tools such as PC-Lint throw a warning with good reason?
  2. Is there a better way to traverse this data?

Edit: Thanks to Lev M. I have updated my code to properly traverse the data using the size of the data type. The circular buffer is meant to be generic meaning we pass in a buffer and a data type size.

The updated test code is as follows.

#include<stdio.h>
int main()
{
    int a[2] = {2, 1};
    void *ptr = &a;
    for(int i=0; i<2; i++) {
        printf("%d\n", *(int *)ptr);
        ptr = ptr + sizeof(int);
    }

    return 0;
}
Michael
  • 399
  • 2
  • 17
  • 5
    Why are you using the void pointer in the first place? Use `int *ptr = a;`. Note also that you don't need `&` -- the array decays to a pointer to the first element. – Barmar Mar 09 '22 at 21:35
  • In my example I noted I am using a generic circular buffer. The ints here are just for show. – Michael Mar 09 '22 at 21:42
  • The generic code wouldn't have an explicit cast to `int *`, since it needs to work with any type. Take a look at how `qsort()` works. It gets a `void*` pointer and the size of the elements. It converts it to `char*` so it can add the size to the pointer to traverse. The actual type is used only in the comparison function, which has to be specific to the type. – Barmar Mar 09 '22 at 21:45
  • Change your array to `{2,1}` and see what the output is. – Sneftel Mar 09 '22 at 21:48
  • @Michael *In my example I noted I am using a generic circular buffer.* You can't have a "generic" buffer - circular or otherwise. All objects in C that can be instantiated must have a concrete type. And that's not `void`. – Andrew Henle Mar 09 '22 at 22:16
  • @Barmar Even the "generic" `qsort()` must be provided with a function pointer to a function that operates not on anything "generic" but on specific object types. – Andrew Henle Mar 09 '22 at 22:19
  • So there is no way to create a circular buffer that can be instantiated to hold ints, chars, or structs? Then given the size of the data type proper add/remove and traverse that data? – Michael Mar 09 '22 at 22:19
  • @Michael If you're holding a `void *` (note that's a *pointer* to `void` - a *concrete* type) you have to hold additional data so you know what actual type you're referring to. – Andrew Henle Mar 09 '22 at 22:20
  • I am stuck at the same spot the author of this question is at. https://stackoverflow.com/questions/69337684/circular-buffer-with-void-pointer-parameter. There is a void* data type that is pointing to an array. In the print or add functions I know the object size and memory location. – Michael Mar 09 '22 at 22:21
  • 1
    @AndrewHenle That's the "comparison function" that I mentioned in my comment. – Barmar Mar 09 '22 at 22:25
  • @Michael Take a look at how [C generics](https://port70.net/~nsz/c/c11/n1570.html#6.5.1.1) work: https://stackoverflow.com/questions/9804371/syntax-and-sample-usage-of-generic-in-c11 You need to do something similar - handle different types of data, and then record in some manner what that type is when you store a `void *` pointer to your data so you know what it is when you have to access it. – Andrew Henle Mar 09 '22 at 22:26
  • (cont) There's a reason other languages have functionality to handle data of a type that's not specified at compile-time... – Andrew Henle Mar 09 '22 at 22:28
  • 1
    Note that even if you solve the traversal problem, you run into another problem: You can't print the data if you don't know the type. – Barmar Mar 09 '22 at 22:30

2 Answers2

2

There is a major bug in your code:

printf("%d\n", *(int *)ptr+i);

The operator precedence means this statement will first do *(int*)ptr than add 2 to the value in the first cell of the array.

If you actually wanted to traverse the array, you would need to do this:

printf("%d\n", *(int *)(ptr+i));

But, that would immediately throw a compiler warning:

warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arith]

This is for a very good reason: to do pointer arithmetic, your compiler needs to know the size of the data being pointed to.
But void has no size for its data, there for this is illegal in standard C.

It is supported by some compiler extensions: http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html

But note than in case of GCC, the size is 1, so your array will be treated as array of char not int.

How to traverse the buffer in your application will depend on whether the data in it is uniform or not.

You will need to present actual data example if you want a working traversal solution.

Lev M.
  • 6,088
  • 1
  • 10
  • 23
  • Thank you for your comprehensive answer. I now understand the issue and have used the example given and your answer to update my code. I will edit my question to reflect this. – Michael Mar 09 '22 at 21:56
  • 1
    I think the correct expression is `*((int *)ptr + i)` or `((int *)ptr)[i]` – Barmar Mar 09 '22 at 22:27
0

I'm not sure what you want to do we this array but basically the way you can't 'use' void* unless you convert it to its true type. To do so what you wrote above is just fine ((int*)ptr) once you did that you can use this to do any legal operation you want with this 'int' pointer.

Sharon
  • 56
  • 3
  • I do not think what I want to do matters, the question remains valid. Is there a different way to do this? Is this code "safe"? Will tools such as PC-Lint throw warnings? – Michael Mar 09 '22 at 21:43