0

I'm trying to create a program which takes an integer value which then determines the amount of strings that can be inputted, this is looped twice. Once the two sets of strings have been inputted my program should sort them and output them based on their lengths in descending order. When I output the results I don't get what I should be getting.

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

int main(void)
{

    int i, num_of_str;
    //int j;
    //int a;
    //int n;
    char input[100];

    scanf("%d", &num_of_str);

    char** strings = malloc(num_of_str * sizeof(char*));

    for (i = 0; i < num_of_str; i++) {

        fgets(input, 100, stdin);
        strings[i] = malloc(strlen(input) * sizeof(char*));
        strcpy(strings[i], input);
    }

    scanf("%d", &num_of_str);

    for (i = 0; i < num_of_str; i++) {

        fgets(input, 100, stdin);
        strings[i] = malloc(strlen(input) * sizeof(char*));
        strcpy(strings[i], input);
    }

    int a;
    int b;
    char* temp;

    for (a = 0; a < num_of_str; a++) {
        for (b = a + 1; b < num_of_str; b++) {
            if (strlen(strings[a]) < (strlen(strings[b]))) {

                temp = strings[a];
                strings[a] = strings[b];
                strings[b] = temp;
            }
        }
    }

    for (a = 0; a < num_of_str; a++) {
        printf("%s\n", strings[a]);
    }
    return EXIT_SUCCESS;
}
16
  • 1
    and what is your result? aside: strings[i] = malloc(strlen(input) * sizeof(char*)); is missing 1 for null-termination char, and the sizeof(char*) error makes up for it but allocating 4 or 8 times the required size... strings[i] = malloc(strlen(input)+1); would be correct. Commented Mar 2, 2017 at 10:07
  • 2
    main problem: you're overwriting your first set of strings with the other one. Why making 2 sets BTW? Commented Mar 2, 2017 at 10:08
  • @Jean-FrançoisFabre for example if I input 5: abcdefghijk abcde a abcdefgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa zzzzzzzzzzzzzzzzzzzzzz 3: bb cccc hhhhhhhhhhhhhh . I get the output of: hhhhhhhhhhhhhh cccc bb 3. Commented Mar 2, 2017 at 10:13
  • @SideWaysCoding don't post complementary information in comments but edit your question. Commented Mar 2, 2017 at 10:16
  • 1
    Bug: malloc(strlen(input) * sizeof(char*)); --> malloc(strlen(input) + 1); to allocate room for \0 and the sizeof is unnecessary and should not be sizeof pointer. Commented Mar 2, 2017 at 10:23

2 Answers 2

1

Here are some of the issues in your code:

  • Return pointer malloc() is not being checked. It needs to be checked as the void* pointer from it can return NULL if unsuccessful. You can check malloc() like this:

    ptr = malloc(......);
    if (ptr == NULL) {
        /* handle error */
    }
    
  • Return value of scanf() is not being checked. This needs to check if 1 integer value for numstr was found. You can verify this like:

    if (scanf("%d", &numstr) != 1) {
        /* handle error */
    }
    
  • strings[i] is not being allocated properly. Since this is a char* pointer, you need to allocate a number of char bytes, not char* pointers. You also need to add +1 to your allocation, to ensure their is enough space for the null-terminator \0. So instead of:

    strings[i] = malloc(strlen(input) * sizeof(char*));
    

    You can do:

    strings[i] = malloc(strlen(input)+1);
    

    Note: sizeof(char) is always 1, so no need to include it here.

  • Your code does not update **strings on the second numstr. This will lead to issues if numstr ends up being a bigger, in which you will be accessing beyond the limit of **strings. You may need to use realloc(3) here, to resize your block of memory. A way you can do this is to keep track of the first numstr, then check it against the second numstr, and if they are different, resize strings. Here is an example:

    prev_numstr = numstr;
    
    printf("Enter number of strings for set 2:\n");
    if (scanf("%zu ", &numstr) != 1) {
        /* handle exit */
    }
    
    if (numstr != prev_numstr) {
        void *temp = realloc(strings, numstr * sizeof(*strings));
        if (temp == NULL) {
            /* handle exit */
        }
        strings = temp;
    } 
    
  • Since scanf() leaves a \n character in the input buffer, you need to get rid of this before your call to fgets(). You can simply add a space by doing scanf("%d ", &num_of_str), which will consume any white space still left in the buffer.

  • fgets() must be checked, as it can return a NULL pointer on failure to read a line. It also appends a \n character at the end of the buffer, so you might need to remove this newline sometime.

  • Any heap allocation made with malloc() and realloc() must be de-allocated with free(3) at the end.

  • Since void *malloc(size_t size) expects size_t, it is better to use size_t variables here instead.

Since your code doesn't have issues with sorting, here is some example code that uses these points:

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

#define LINESIZE 100

int main(void) {
    char input[LINESIZE];
    size_t numstr, prev_numstr, slen;
    void *temp = NULL;

    printf("Enter number of strings for set 1:\n");
    if (scanf("%zu ", &numstr) != 1 || numstr < 1) {
        fprintf(stderr, "Invalid value\n");
        exit(EXIT_FAILURE);
    }

    char **strings = malloc(numstr * sizeof(*strings));
    if (strings == NULL) {
        fprintf(stderr, "Cannot allocate %zu strings\n", numstr);
        exit(EXIT_FAILURE);
    }

    /* Set 1 */
    for (size_t i = 0; i < numstr; i++) {
        if (fgets(input, LINESIZE, stdin) != NULL) {
            slen = strlen(input);

            /* removes newline */
            if (slen > 0 && input[slen-1] == '\n') {
                input[slen-1] = '\0';
            }

            strings[i] = malloc(strlen(input)+1);
            if (strings[i] == NULL) {
                fprintf(stderr, "Cannot allocate %zu bytes for string\n", strlen(input)+1);
                exit(EXIT_FAILURE);
            }

            strcpy(strings[i], input);
        }       
    }

    /* keeps track of previous number of strings */
    prev_numstr = numstr;

    printf("Enter number of strings for set 2:\n");
    if (scanf("%zu ", &numstr) != 1 || numstr < 1) {
        fprintf(stderr, "Invalid value\n");
        exit(EXIT_FAILURE);
    }

    /* only enters if size is different */
    if (numstr != prev_numstr) {
        temp = realloc(strings, numstr * sizeof(*strings));
        if (temp == NULL) {
            fprintf(stderr, "Cannot reallocate %zu spaces\n", numstr);
            exit(EXIT_FAILURE);
        }
        /* perhaps temp could could freed here */
        strings = temp;
    } 

    /* Set 2 */
    for (size_t i = 0; i < numstr; i++) {
        if (fgets(input, LINESIZE, stdin) != NULL) {
            slen = strlen(input);
            if (slen > 0 && input[slen-1] == '\n') {
                input[slen-1] = '\0';
            }

            strings[i] = malloc(strlen(input)+1);
            if (strings[i] == NULL) {
                fprintf(stderr, "Cannot allocate %zu bytes for string\n", strlen(input)+1);
                exit(EXIT_FAILURE);
            }

            strcpy(strings[i], input);
        }       
    }

    /* printing and freeing strings */
    for (size_t i = 0; i < numstr; i++) {
        printf("%s\n", strings[i]);
        free(strings[i]);
        strings[i] = NULL;
    }

    /* freeing double pointer 'strings' itself */
    free(strings);
    strings = NULL;

    exit(EXIT_SUCCESS);
}

Note: I assumed you still wanted to take in 2 sets, and overwrite the first one with the second one, which does seem odd, however.

Sign up to request clarification or add additional context in comments.

Comments

0

I'm not sure why you are taking 2 sets,one thing I wanted to mention you are using scanf for getting no of strings & fgets for string. So, you may loose 1st entry because of this.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.