Thread: passing an array of structures containg pointers to a function, for sorting

  1. #1
    Registered User
    Join Date
    Jul 2012
    Location
    Australia
    Posts
    242

    passing an array of structures containg pointers to a function, for sorting

    Hi all.

    I have an array of structures:

    Code:
    struct word
    {
        char *word;
        int wordcount;
    } array[ARRAYSIZE];
    I have a function sort() that will use qsort() to sort the pointers in the structure. I want to pass the array to the function. What should the prototype for the function and the function call look like?

    I have create the function prototype as:

    Code:
    void sort(struct word array);
    And tried to pass with:

    Code:
    sort(array);
    And the compiler is giving the error message: Incompatible type for argument 1 of 'sort'.

    I know the problem is that I need to include the reference to the pointer as a parameter in the function prototype and as an argument in the function call. But I don't know how to implement this. My reference book and googling has not provided any insights.

    I've tried with no success, probably because the parameters in the function prototype is wrong:
    Code:
    sort(array.word)
    Help? Thank you.

    Code:
    // wordcount.c: count the number of occurrences of every word in a text file
    // 4 functions: openfile, countwords, sort, display
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define FILENAMELENGTH 30
    #define ARRAYSIZE 1000
    #define SUCCESS 1
    #define FAILURE 0
    
    FILE *file;
    int arraycount = 0;
    
    struct word
    {
        char *word;
        int wordcount;
    } array[ARRAYSIZE];
    
    int openfile(char *filename);
    void countwords(void);
    void sort(struct word array);
    //void display(char *array);
    
    int main(void)
    {
        int result;
        char filename[FILENAMELENGTH];
    
        do
        {
            printf("Enter file to open: \n");
    
            fgets(filename, FILENAMELENGTH, stdin);
            filename[strlen(filename) - 1] = '\0';
        } while((result = openfile(filename)) == FAILURE);
    
        countwords();
        sort(array);
    //    display(filename);
    
        return 0;
    }
    
    int openfile(char *filename)
    {
        if((file = fopen(filename, "r")) == NULL)
            return FAILURE;
        else
        {
            printf("%s opened successfully\n", filename);
            return SUCCESS;
        }
    }
    
    void countwords(void)
    {
        #define WORDLENGTH 30
    
        char buffer[WORDLENGTH];
        int flag,c, count, buffercount = 0;//declared a variable flag
    
        for(count = 0; count < ARRAYSIZE; count++)  // initialise array member wordcount
        {
            array[count].wordcount = 0;
        }
    
        while((c = fgetc(file)) != EOF)
        {
            if((c != '.') && (c != ',') && (c != '!') && (c != ' ') && (c != ':') && (c != '(') && (c != ')') && (c !=
             '-') && (c !='\n'))//check for enter in order to read the last word properly(without the '\n' considered to be part of the word)
            {
                buffer[buffercount] = c;
                buffercount++;
                continue;
            }
    
            else
            {
                buffer[buffercount] = '\0';     // if c is a space or punctuation, add terminator to create string
    
                if(arraycount == 0) // add first word to first array
                {
                    array[arraycount].word = malloc((buffercount) * sizeof(char));  // allocate memory for string
                    strncpy(array[arraycount].word, buffer, buffercount);
                    array[arraycount].wordcount++;  // increment word counter
                    arraycount++;       // increment the word array to accept new word
                    buffercount = 0;    // reset buffer to accept new string
                    continue;
                }
                flag=0;//flag set to zero,before every loop for checking about duplicates
                for(count = 0; count < arraycount; count++)
                {
                    if((strcmp(buffer, array[count].word)) == 0)
                    {
                        array[count].wordcount++;   // increment word counter
                        buffercount = 0;    // reset buffer to accept new string
                        flag=1;// we have a duplicate,set flag to true
                        break;
                    }
                }
    
                    if(!flag) //flag is 0,so new word occured; add new word to array.
                    {
    
                        array[arraycount].word = malloc((buffercount) * sizeof(char));  // alocate memory
                        strncpy(array[arraycount].word, buffer, buffercount);
                        array[arraycount].wordcount++; // increment word counter
                        arraycount++;   // increment the word array to accept new word
                        buffercount = 0;    // reset buffer to accept new string
                    }
            }
        }
    
        fclose(file);
    }
    
    void sort(struct word array)
    {
        qsort(array, arraycount, sizeof(array[0]), strcmp)
    }
    
    void display(void)
    {
        for(count = 0; count < arraycount; count++)
        {
             printf("%s %d\n", array[count].word, array[count].wordcount);
             free(array[count].word);
        }
    }
    Last edited by cfanatic; 08-05-2012 at 12:36 AM.
    IDE: Code::Blocks | Compiler Suite for Windows: TDM-GCC (MingW, gdb)

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,604
    The only things that matter are that it is a structure, and that it is an array.
    What the structure contains is irrelevant.

    Code:
    struct word
    {
        char *word;
        int wordcount;
    } array[ARRAYSIZE];
    
    void sort(struct word array[ ]);
    And you call it using
    sort( array );
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    'sort' is going to need half of the arguments that you're currently passing to qsort anyway. If it were me, I would just call qsort directly from main.
    You're not really gaining anything since you're only calling it once anyway.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  4. #4
    Registered User
    Join Date
    May 2012
    Posts
    1,066
    Code:
    do
    {
         printf("Enter file to open: \n");
    
        fgets(filename, FILENAMELENGTH, stdin);
        filename[strlen(filename) - 1] = '\0';
    } while((result = openfile(filename)) == FAILURE);
    Same bug as three weeks before

    Code:
    if((c != '.') && (c != ',') && (c != '!') && (c != ' ') && (c != ':') && (c != '(')
        && (c != ')') && (c != '-') && (c !='\n'))//check for enter in order to read the last word properly(without the '\n' considered to be part of the word)
    Have a look at the ctype.h character classifying functions. I think you could use ispunct() for simplifying your if-check.
    IMHO it's better to use a "whitelist" of characters you want to accept in a word (probably isalpha() or isalnum()) instead of listing all characters you don't want.

    Code:
    buffercount++;
    You never check in your code if buffercount is already equal to WORDLENGTH -> buffer overrun if you encounter words with more than 29 characters.

    Code:
    void sort(struct word array)
    {
        qsort(array, arraycount, sizeof(array[0]), strcmp)
    }
    You will probably notice sooner than later that you can't use strcmp as the comparison function :-) (I mean not directly as a parameter to qsort)
    Your array's elements are structures and thus you have to write your own comparison function (basically a wrapper for strcmp) because strcmp just works with strings.

    Bye, Andreas

  5. #5
    Registered User
    Join Date
    Jul 2012
    Location
    Australia
    Posts
    242
    Yes I know about the filename length checking. The files I want to open have filenames less than 10 characters(including terminator). Same with buffercount++, I know that there will be no words that are more than 29 characters. The program was really only for me to practice functions and passing pointers to functions. Thats why I created a function to sort instead of just doing the sort within main().


    I didn't want to spend time checking for problems that I know I won't be having. In another post I explained that I would definitely be checking in serious programs.

    Anyway, here is the complete program, which works. But I am confused about the casting of a pointer to a pointer in the comp() function.

    The parameter of the comp() function is:

    Code:
    const void *word2
    Why is the argument of strcmp():
    Code:
    *(char **)word2
    I know that the void has been cast to a char, and that the array name is a pointer, and the string is a pointer, hence the **(pointer to pointer). But why *(char **)? Shouldn't it be: charr **word2 (pointer to pointer of type char)? I think that operator precedence might be involved, but still can't explain the * at the front.

    If someone could explain in english what *(char **)word2 really means, I would appreciate it. I got the idea from a book, so even though I know how to use it, I am confused about what it actually means.

    Thanks.

    Code:
    // wordcount.c: count the number of occurrences of every word in a text file
    // 4 functions: openfile, countwords, sort, display
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define FILENAMELENGTH 30
    #define ARRAYSIZE 1000
    #define SUCCESS 1
    #define FAILURE 0
    
    FILE *file;
    int arraycount = 0;
    
    struct word
    {
        char *word;
        int wordcount;
    } array[ARRAYSIZE];
    
    int openfile(char *filename);
    void countwords(void);
    void sort(struct word array[]);
    void display(void);
    int comp(const void *word1, const void *word2);
    
    int main(void)
    {
        int result;
        char filename[FILENAMELENGTH];
    
        do
        {
            printf("Enter file to open: \n");
    
            fgets(filename, FILENAMELENGTH, stdin);
            filename[strlen(filename) - 1] = '\0';
        } while((result = openfile(filename)) == FAILURE);
    
        countwords();
        sort(array);
        display();
    
        return 0;
    }
    
    int openfile(char *filename)
    {
        if((file = fopen(filename, "r")) == NULL)
            return FAILURE;
    
        else
        {
            printf("%s opened successfully\n", filename);
            return SUCCESS;
        }
    }
    
    void countwords(void)
    {
        #define WORDLENGTH 30
    
        char buffer[WORDLENGTH];
        int flag,c, count, buffercount = 0;//declared a variable flag
    
        for(count = 0; count < ARRAYSIZE; count++)  // initialise array member wordcount
        {
            array[count].wordcount = 0;
        }
    
        while((c = fgetc(file)) != EOF)
        {
            if((c != '.') && (c != ',') && (c != '!') && (c != ' ') && (c != ':') && (c != '(') && (c != ')') && (c !=
             '-') && (c !='\n'))//check for enter in order to read the last word properly(without the '\n' considered to be part of the word)
            {
                buffer[buffercount] = c;
                buffercount++;
                continue;
            }
    
            else
            {
                buffer[buffercount] = '\0';     // if c is a space or punctuation, add terminator to create string
    
                if(arraycount == 0) // add first word to first array
                {
                    array[arraycount].word = malloc((buffercount) * sizeof(char));  // allocate memory for string
                    strncpy(array[arraycount].word, buffer, buffercount);
                    array[arraycount].wordcount++;  // increment word counter
                    arraycount++;       // increment the word array to accept new word
                    buffercount = 0;    // reset buffer to accept new string
                    continue;
                }
                flag=0;//flag set to zero,before every loop for checking about duplicates
                for(count = 0; count < arraycount; count++)
                {
    
                    if((strcmp(buffer, array[count].word)) == 0)
                    {
                        array[count].wordcount++;   // increment word counter
                        buffercount = 0;    // reset buffer to accept new string
                        flag=1;// we have a duplicate,set flag to true
                        break;
                    }
                }
    
                    if(!flag)//flag is 0,so new word occured
                    {
                        array[arraycount].word = malloc((buffercount) * sizeof(char));  // alocate memory
                        strncpy(array[arraycount].word, buffer, buffercount);
                        array[arraycount].wordcount++; // increment word counter
                        arraycount++;   // increment the word array to accept new word
                        buffercount = 0;    // reset buffer to accept new string
                    }
            }
        }
    
        fclose(file);
    }
    
    void sort(struct word array[])
    {
        qsort(array, arraycount, sizeof(array[0]), comp);
    }
    
    void display(void)
    {
        int count = 0;
    
        for(count = 0; count < arraycount; count++)
        {
             printf("%s %d\n", array[count].word, array[count].wordcount);
             free(array[count].word);
        }
    }
    
    int comp(const void *word1, const void *word2)
    {
        return(strcmp(*(char **)word1, *(char **)word2));
    }
    IDE: Code::Blocks | Compiler Suite for Windows: TDM-GCC (MingW, gdb)

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,619
    If someone could explain in english what *(char **)word2 really means
    You need to learn about type conversion to really understand it. Anyway, since word1 and word2 are void pointers, you have to convert the type of the pointer before you can dereference it. It might be confusing in this case because the destination type is a char **, but that is not really different from any other comp function. The things you are comparing are strings, so it has to be a pointer already, and qsort will send pointers to the strings it wants compared.

    That specific bit of code works because type conversion happens before dereferencing. See operator precedence.
    Last edited by whiteflags; 08-05-2012 at 07:54 PM.

  7. #7
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    929
    You're only lucky that it works because the word pointer is the first member of struct word. You're passing in an array of struct word to the qsort function, so the void pointers should be cast to struct word *, not char**:

    Code:
     return strcmp(((struct word *)word1)->word, ((struct word *)word2)->word);

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 04-04-2012, 01:19 PM
  2. Replies: 5
    Last Post: 05-26-2011, 06:44 AM
  3. Structures, passing array of structures to function
    By saahmed in forum C Programming
    Replies: 10
    Last Post: 04-05-2006, 11:06 PM
  4. passing array of structures to function
    By bvnorth in forum C Programming
    Replies: 3
    Last Post: 08-22-2003, 07:15 AM
  5. Replies: 2
    Last Post: 03-07-2002, 10:14 AM