简体   繁体   中英

How to sort an array of pointers to char

new user to stack exchange here so my apologies if I am asking this question incorrectly.

I have an assignment to create a program to assign lines of text input by the user to an array of character pointers using dynamic memory allocation.

Once all the lines have been entered, the lines are printed back. Then they are manipulated by moving the 5th and 6th lines to the end and the lines inbetween forward ('up'.). Finally, the lines must be sorted alphabetically. My problem lies here: I am having trouble getting a sort function to work on an array of char pointers.

My code is posted below. I am not sure if my sort function is referencing the char pointers properly. I am also not sure if my variables are correct, and I am having trouble determining if I am properly assigning char strings to char pointers in the char pointer array. Can you please help me with these things?

Many thanks, r4mulus

//Program to collect lines of text, shift them in various ways and sort them, and output all the lines at each stage

// Declare libraries

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Declaring macros

#define len1 80

// Prototpying functions

void sortLine(char **, int size);
void swap(char **, char **);


// Main body of program

int main (void){
    char *line_array[40];
    char *(*ptr_all) = &line_array[0];
    char buffer[80];
    int ch;
    int counter = 0;

    // Loop to intake lines of text

    do {
        // Ask user to input a line of text

        printf("Please input a line of text to store into memory. Enter '#' to stop collecting lines:\n");
        fgets(buffer, len1+1, stdin);

        // Check to make sure char '#' has not been entered

        ch = buffer[0];
        if (ch == '#'){
            break;
        }

        // Measure the length of the line of text

        unsigned long len = strlen(buffer);

        // Allocate memory for the given line of text

        line_array[counter] = (char *)malloc(len+1);

        // Copy string in buffer into pointer to array of strings

        strcpy(line_array[counter], buffer);

        // Clear the buffer

        for (int p = 0; p < (len1+1); p++){
            buffer[p] = '\0';
        }

        // Increment the counter

        counter++;

    } while (counter < 40);
    printf("\n");

    // Print the lines collected so far

    for (int q = 0; q < counter; q++){
        printf("%s", line_array[q]);
    }
    printf("\n");

    // Move lines of text 5 and 6 to the end; Move all other following lines forward two line spaces
    char *temp_ptr1;
    char *temp_ptr2;
    temp_ptr1 = line_array[4];
    temp_ptr2 = line_array[5];
    for ( int r = 4; r < counter; r++){
        line_array[r] = line_array[r+2];
    }
    line_array[counter-2] = temp_ptr1;
    line_array[counter-1] = temp_ptr2;

    // Print the rearranged lines

    for (int s = 0; s < counter; s++){
        printf("%s", line_array[s]);
    }
    printf("\n");

    // Sort the lines alphabetically

    sortLine(ptr_all, counter);

    // Print the lines sorted alphabetically

    for (int t = 0; t < counter; t++){
        printf("%s", line_array[t]);
    }
    printf("\n\n");

    // End the program

    return 0;
}

Functions

// Function to sort elements in an array

void sortLine(char **a, int size){
    int i, j, compare;
    for (i = 0; i < size; i++){
        for (j = i; j < size; j++){
            compare = strcasecmp((a[i]), (a[j]));
            if (compare > 0)
                swap(&a[i], &a[j]);
        }
    }
}

// Function to swap elements in an array

void swap(char **a, char **b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

Counting on the fact that newline will be less than any text character, you can use memcmp to compare lines.

void swap(char **a, char **b);

void sortLine(char *a[], int size){
    int i, j;
    for (i = 0; i < size; i++){
        for (j = i + 1; j < size; j++){
            if(memcmp(a[i], a[j], 80) > 0){
                swap(&a[i], &a[j]);
            }
        }
    }
}

void swap(char **a, char **b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

The call should be:

    sortline(line_array, counter);

Your code has some issues, expecially when dealing with pointers and allocation, but most of all, you have to divide it in more simplier and usefull functions. For example you repeted several times the code to print the lines, when you should write a function instead:

void printLines( char ** ppc, unsigned int n ) {
    for (int i = 0; i < n; i++) {
        printf("%s", ppc[i]);
    }
    printf("\n");     /* Adds always an empty line at end */
}

You were assigned to use dynamic allocation, and you apparently wanted to store the lines in an array of null terminated array of char, char** , big enough to store 40 (I think) lines of max 80 character. But then, when you declare char *line_array[40]; you allocate a pointer to an array of 40 chars, not 40 pointers to char. Write some functions instead:

char ** allocLines( unsigned int n ) {
    char ** ppc = (char **) malloc(n * sizeof(char *));
    if ( !ppc ) {
        printf("Error, unable to allocate memory for %d lines, malloc returned a null pointer!\n",n);
        exit(1);
    }
    return ppc;       
}

char * allocLine( unsigned int n ) {
    char * pc = (char *) malloc((n + 1) * sizeof(char));
        if ( !pc ) {
            printf("Error, unable to allocate memory for %d chars, malloc returned a null pointer!\n",n + 1);
            exit(1);
        } 
}

void freeLines( char ** ppc, unsigned int n ) {
    if ( ppc ) {
        for ( int i = 0; i < n; i++ )
            if ( ppc[i] ) free(ppc[i]);
        free(ppc);
    }
}

And use them in another function to read the lines:

unsigned int readLines( char ** ppc, unsigned int max, FILE * stream ) {
    unsigned int counter = 0;
    unsigned int length_of_line;
    char buffer[MAX_LINE_LENGTH];
    char *pc;

    printf("Please, input up to %d lines of text to store into memory. Enter '#' to stop collecting lines:\n", max);
    while ( counter < max
            && fgets(buffer, MAX_LINE_LENGTH, stream) ) {
                                            /* fgets return NULL if no character is read when reaching EOF. Usefull if you read a file instead of stdin */
        if ( buffer[0] == '#' ) break;      /* stop reading lines if user enter # */

        length_of_line = strlen(buffer);
        pc = allocLine(length_of_line);     /* It automatically adds the extra space for the '\0' terminator */                    
        strcpy(pc,buffer);            /* You can easily write your own version of strlen() and strcpy() if you're not allowed to use too many library functions */
        ppc[counter] = pc;            /* You don't really need pc, you can use ppc[counter] directly */
                                      /* Why do you clear the buffer? its values are overwritten by fgets() */ 
        counter++;                   
    }
    return counter;
}

Now, to sort the lines you need to compare two string, but insted of rewriing srtcmp I think you can write a more specialized function:

char isFirstStringGreater( char * a, char * b ) {
    int i = 0;
    while(a[i]!='\0' && b[i]!='\0') {
        if ( a[i] < b[i] ) return 0;
        else if ( a[i] > b[i] ) return 1;
        i++;
    }
    return  a[i]=='\0' ? 0 : 1;
}

And use it in your sort algorythm. Which by the way is pretty inefficient, but sooner or later you will learn (or google) better alternatives.

void sortLines(char ** a, int n ) {
    int i, j;
    for (i = 0; i < n; i++){
        for (j = i + 1; j < n; j++) {
            if ( isFirstStringGreater(a[i], a[j]) ) 
                swap(&a[i], &a[j]);
        }
    }
}

The swap() you wrote, as I already mentioned, didn't really swap the arrays.

void swap( char ** a, char ** b ) {
    char * temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

To slide the elements as you are asked to do, you can think to generalize the previous function:

void shiftNLinesToEnd( char ** ppc, unsigned int s, unsigned int n, unsigned int max ) {
    char ** temp = allocLines(n);
    unsigned int i;

    for ( i = 0; i < n; i++ )
        temp[i] = ppc[s+i];
    for ( i = s + n; i < max; i++ ) /* it would be a good idea to check if max > n + s... */
        ppc[i-n] = ppc[i];
    for ( i = 0; i < max; i++ )
        ppc[max - n + i] = temp[i];
    free(temp);
}

Now you can write a cleaner and more readable (I think) main():

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void swap( char ** a, char ** b);
void sortLines( char ** a, int size);
char isFirstStringGreater( char * a, char * b );
unsigned int readLines( char ** ppc, unsigned int max, FILE * stream );
void printLines( char ** ppc, unsigned int n );
void shiftNLinesToEnd( char ** ppc, unsigned int s, unsigned int n, unsigned int max );
char ** allocLines( unsigned int n );
char * allocLine( unsigned int n );
void freeLines( char ** ppc, unsigned int n );

#define MAX_LINE_LENGTH     82    /* enough space to store up to 80 character plus '\n', plus the '\0' */
#define MAX_N_OF_LINES      40

int main(void) {
    char **lines;
    unsigned int n_of_lines;

    lines = allocLines(MAX_N_OF_LINES);
    n_of_lines = readLines(lines, MAX_N_OF_LINES,stdin);

    printf("\n%d lines read:\n\n",n_of_lines);                           
    printLines(lines, n_of_lines);

    shiftNLinesToEnd(lines, 4, 2, n_of_lines);
    printf("Rearranged, lines 5 and 6 have been moved to the end:\n\n");
    printLines(lines, n_of_lines);

    sortLines(lines, n_of_lines);
    printf("Alphabetically sorted:\n\n");
    printLines(lines, n_of_lines);

    freeLines(lines, n_of_lines);       /* free memory before finish */
    return 0;
}

Hope it helped.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM