1

I intend to understand the working of function pointers in C.I wrote this program for a function which has a function pointer disp_function that is expected to printed the contents of an array of any type,using the pointer ptr to an intended destination,but it won't work:

void show_array(void* ptr, void (*disp_function)(void*), int nr_elem) {
    int i=0;
    while(i<nr_elem) {
        (*disp_function) (ptr);
        ptr++;
        i++;
    }
}

Here is an instance of the use of the program:

void show_movements(Movement* movs, int nr) {
    void (*display_function) (void* ptr) = show_movement;
    show_array(movs, display_function, nr);
}

void show_movement(void* ptr) {
    Movement* ptr2 = NULL;
    ptr2 = (Movement*) ptr;
    printf("%d -> '%s'\n", ptr2->id, ptr2->title);
}

The program crashes in the last function.Here are the specifics of it:

  • the array has several elements (> 2)
  • the first one is printed correctly
  • the program crashes when attempting to print the second element (verified by debugging)

I suppose it's the ptr++ which is causing the crash as I am incrementing a void*.Also I tried using array syntax (*display_function) (ptr[i]); but I always get an error invalid use of void expression

Can anyone give me an idea what's the exact cause of the problem?

Rüppell's Vulture
  • 3,583
  • 7
  • 35
  • 49
MyName
  • 2,136
  • 5
  • 26
  • 37

3 Answers3

3

When incrementing a void* pointer, C must guess what your data size is. It's always going to assume that the data is of the same size as char, which is also called the Minimum Allocatable Unit (MAU). What you should do is also pass in the size of your data and do the following:

void show_array(void* ptr, void (*disp_function)(void*), int nr_elem, size_t data_size) {
    int i=0;
    while(i<nr_elem) {
        (*disp_function) (ptr);
        ptr += data_size;
        i++;
    }
}

And the size of your data should be passed in as follows:

void show_movements(Movement* movs, int nr) {
    void (*display_function) (void* ptr) = show_movement;
    show_array(movs, display_function, nr, sizeof(Movement) );
}
Phonon
  • 12,549
  • 13
  • 64
  • 114
  • 2
    Actually, neither the C nor C++ standard permit arithemtic on `void` pointers. GCC allows it as an extension. Other compilers do not allow it (MSVC, for example). – Dietrich Epp May 11 '13 at 00:39
  • @DietrichEpp Then perhaps casting it to `char*` is the right way to do it in this case? – Phonon May 11 '13 at 20:46
1

use typedef to declare a function pointer type

typedef void (*disp_function)(void*);

now it is a type, it can be part of the function parameters. also void * cannot be incremented

void show_array(void* ptr, disp_function f, int nr_elem) {
    int i=0;
    while(i<nr_elem) {
        f(ptr);
        //ptr++; // void * cannot be incremented without casting to a concrete type
        i++;
    }
}

void show_movement(void* ptr) {
    Movement* ptr2 = NULL;
    ptr2 = (Movement*) ptr;
    printf("%d -> '%s'\n", ptr2->id, ptr2->title);
}

simply pass show_movement into show_array

void show_movements(Movement* movs, int nr) {
    show_array(movs, show_movement, nr);
}
yngccc
  • 5,594
  • 2
  • 23
  • 33
  • +1. Thanks. This solution (also) works, and allows for a clean interface. However, its not obvious to me why show_movements() can be simplified. Can you explain that a little more? Thanks. – MyName May 11 '13 at 00:48
  • because `show_movement` is both a function and a function pointer, since `show_array` expected a function pointer as second arguments, u can simply pass `show_movement` – yngccc May 11 '13 at 00:54
  • I think there's still an error in show_array(). It currently always displays the first element. If ptr is not incremented, how is f(ptr) going to work? – MyName May 12 '13 at 15:31
1

Void pointers can be tricky when you are doing pointer arithmetic and array syntax on them. A good answer to your question is in the answers here: Pointer arithmetic for void pointer in C

Since void pointers are considered to be equivalent to byte pointers for purposes of pointer arithmetic, you are incremented by 1 rather than by the size of your type.

Community
  • 1
  • 1
LordOphidian
  • 178
  • 9
  • 2
    Actually, the standard does not allow arithmetic on `void` pointers. GCC allows this as an extension. Other compilers do not (MSVC, for example). You have to read to the very bottom of the answer you linked to before you see that conclusion, however. – Dietrich Epp May 11 '13 at 00:37
  • @DietrichEpp Oops. I did miss that. It looks like it works in clang too. – LordOphidian May 11 '13 at 00:48