Thread: Passing an array or pointers to the same function as a pointer

  1. #1
    Registered User
    Join Date
    Oct 2009
    Posts
    21

    Passing an array or pointers to the same function as a pointer

    Hi all,

    I'm building a program that reads data in from a file and inputs it into both a linked list and hash table (for time comparisons). I have a function to which i pass a file name and a function pointer to a function that adds the data to the data structure (either a function to append to the list or a function to insert into a hash table). The problem is the function prototype looks like this:

    Code:
    void add(struct list_node **, char *, int)
    where the char* is the value to add and the int the number of occurences. This works fine for a linked list which is declared as:

    Code:
    struct node *list = NULL;
    however obviously when i try to pass the hash table declared as:

    Code:
    struct node **table = NULL;
    it doesn't work! How can I get around this?

    Cheers,
    James

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You could make the data-structure-dependent argument a void* pointer. Then your code could read as
    Code:
    void add_linked_list(void *data, char *x, int y) {
        struct list_node **list = data;
        /* ... the rest of the code follows */
    }
    That way the function signatures will match, and you can pass in either type of pointer in the calling code, and the compiler will be happy. Does that solve your problem?
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Oct 2009
    Posts
    21
    This was what I tried originally however I got errors saying the pointer type was incompatible. I tried doing a cast '(struct list_node *)' but this still didn't work. It all works fine except for the fact that the linked list is a pointer and the hash table is an array of pointers.

  4. #4
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by james123 View Post
    This was what I tried originally however I got errors saying the pointer type was incompatible.
    Probably because you are compiling with a C++ compiler instead of a C compiler.

    Quote Originally Posted by james123 View Post
    I tried doing a cast '(struct list_node *)' but this still didn't work. It all works fine except for the fact that the linked list is a pointer and the hash table is an array of pointers.
    If I understand you correctly, you are attempting to write a function which can take a linked list or a hash table. If that's the case, it's obvious that you can't just cast one of them to the other. You need some sort of generic iterator that you can pass instead of either container type. Since this is C and not C++, that means you would need to write your own iterator. It would probably be a whole lot easier just to have 2 separate functions for the 2 data types.
    bit∙hub [bit-huhb] n. A source and destination for information.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Well, a void* pointer can be assigned to either type. Here's an example of what you could do, if I've understood your situation properly. If not, post more code.
    Code:
    #include <stdio.h>
    
    typedef void (*print_t)(void *array, int elements);
    
    void print_int(void *array, int elements) {
        int *data = array;
        int x;
        for(x = 0; x < elements; x ++) {
            printf("%d", data[x]);
            if(x + 1 < elements) printf(" ");
        }
        printf("\n");
    }
    
    void print_int_int3(void *array, int elements) {
        int (*data)[3] = array;
        int x, y;
        for(x = 0; x < elements; x ++) {
            for(y = 0; y < 3; y ++) {
                printf("%d", data[x][0]);
                if(y + 1 < 3) printf(" ");
            }
            printf("\n");
        }
    }
    
    int main() {
        int array[] = {1, 2, 3, 4, 5};
        int positions[][3] = {{1, 1, 1}, {2, 3, 4}, {-1, 5, -16}};
        
        print_t func1 = &print_int;
        print_t func2 = &print_int_int3;
        
        printf("Normal array:\n");
        (*func1)(array, sizeof(array) / sizeof(*array));
        printf("Array of 3D positions:\n");
        (*func2)(positions, sizeof(positions) / sizeof(*positions));
        
        return 0;
    }
    Okay, that's pretty terrible code, but I think you get what I'm try to demonstrate . . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User
    Join Date
    Oct 2009
    Posts
    21
    Quote Originally Posted by bithub View Post
    Probably because you are compiling with a C++ compiler instead of a C compiler.
    I'm using gcc on windows with the following flags: -Wall -Wextra -pedantic -ansi

    Quote Originally Posted by bithub View Post
    If I understand you correctly, you are attempting to write a function which can take a linked list or a hash table. If that's the case, it's obvious that you can't just cast one of them to the other. You need some sort of generic iterator that you can pass instead of either container type. Since this is C and not C++, that means you would need to write your own iterator. It would probably be a whole lot easier just to have 2 separate functions for the 2 data types.
    I have two seperate functions for adding data to the data structures (same linked list functions for hash table collisions) however there doesn't seem any point in rewriting the whole of the file reading function just so that it can accept a different data structure. At present I have these as my main function prototypes:

    Code:
    int build_ds(char *, void (*add)(struct list_node **, char *, int), struct list_node **); // reads in data from file and calls add
    void list_add(struct list_node **, char *, int);
    void list_find(struct list_node *, char *);
    unsigned int hash(char *);
    void table_find(struct list_node *, char *);
    void table_add(struct list_node **, char *, int occurences);
    void table_expand(struct list_node **, int);
    I'm guessing i might need to change the last two to:

    Code:
    void table_add(struct list_node **, char *, int occurences);
    void table_expand(struct list_node **, int);
    but then obviously this will break the other function prototypes seeing as i pass table_add as the second parameter to build_ds.

    Thanks for all the help so far,
    James

  7. #7
    Registered User
    Join Date
    Oct 2009
    Posts
    21
    dwks:

    Just seen your post and it looks like it does what i need. The only thing I don't follow is:

    Code:
    typedef void (*print_t)(void *array, int elements);
    I've never used typedef so not sure what exactly this does!

    Also how would this be done with pointers?

    e.g. struct node *list
    and struct node **table

    Thanks,
    James

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    [edit] This was in response to your first post. I'm just reading your latest. [/edit]

    I don't understand -- you're saying you may have to change the last two prototypes, but according to what you posted they're already changed. (I did a diff in case my eyes were tricking me!)

    Wild guess: was list_add originally
    Code:
    void list_add(struct list_node *, char *, int);
    or perhaps table_add needing to be changed to
    Code:
    void table_add(struct list_node ***, char *, int occurences);
    ?
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Registered User
    Join Date
    Oct 2009
    Posts
    21
    Quote Originally Posted by dwks View Post
    [edit] This was in response to your first post. I'm just reading your latest. [/edit]

    I don't understand -- you're saying you may have to change the last two prototypes, but according to what you posted they're already changed. (I did a diff in case my eyes were tricking me!)

    Wild guess: was list_add originally
    Code:
    void list_add(struct list_node *, char *, int);
    or perhaps table_add needing to be changed to
    Code:
    void table_add(struct list_node ***, char *, int occurences);
    ?
    ooops, yer you're right there was supposed to be another * on the table_add. This would allow me to pass a pointer to an array of pointers but stops me from passing it as an argument to the build_ds function. Is that clearer?

  10. #10
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by james123 View Post
    dwks:

    Just seen your post and it looks like it does what i need. The only thing I don't follow is:

    Code:
    typedef void (*print_t)(void *array, int elements);
    I've never used typedef so not sure what exactly this does!

    Also how would this be done with pointers?

    e.g. struct node *list
    and struct node **table

    Thanks,
    James
    typedef declares a new type. A simple example would be:
    Code:
    typedef int my_int;
    my_int a = 5;
    The code up above is declaring a function pointer type. The name of this new type is print_t, and it can be assigned functions that have the prototype of: void foo(void*, int). For instance:
    Code:
    void my_print(void* array, int elements)
    {
    }
    typedef void (*print_t)(void *array, int elements);
    
    int main(void)
    {
        print_t my_function_pointer;
        my_function_pointer = &my_print; 
        my_function_pointer(NULL, 0); // call the function
        return 0;
    }
    bit∙hub [bit-huhb] n. A source and destination for information.

  11. #11
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    The typedef just "defines a new type". Basically, if I say
    Code:
    typedef int *ip;
    then later I can use the type ip as
    Code:
    ip x;
    which is exactly equivalent to
    Code:
    int *x;
    So by saying
    Code:
    typedef void (*print_t)(void *array, int elements);
    I can later say "print_t func", which is a pointer to a function with the signature void (void*, int).

    typedef isn't necessary in this case, but it can make code easier to read. Your code
    Code:
    int build_ds(char *, void (*add)(struct list_node **, char *, int), struct list_node **); // reads in data from file and calls add
    for example could be changed to
    Code:
    typedef void (*add_func_t)(struct list_node **, char *, int);
    int build_ds(char *, add_func_t, struct list_node **); // reads in data from file and calls add
    ---

    Back on subject. Look at my code carefully: it's using a 1D array (array) and a 2D array (positions). This is basically a pointer as well as a pointer-to-pointer. I think it might be sufficient explanation . . . if not, just post . . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  12. #12
    Registered User
    Join Date
    Oct 2009
    Posts
    21
    Thanks to both of you for explaining that - I thought the build_ds prototype looked pretty ugly! It's been a long day so I'll try your suggestions in the morning and see if i can figure it out with pointers!

    Thanks again,
    James

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 9
    Last Post: 10-15-2008, 03:08 AM
  2. Replies: 2
    Last Post: 07-11-2008, 07:39 AM
  3. c++ linking problem for x11
    By kron in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2004, 10:18 AM
  4. qt help
    By Unregistered in forum Linux Programming
    Replies: 1
    Last Post: 04-20-2002, 09:51 AM
  5. Replies: 2
    Last Post: 03-07-2002, 10:14 AM