0

I need help with the following program:

Print all sorted strings from the input array of strings. Assume that strings are sorted if characters are in lexicographic order (also assume that the only characters in strings are letters).

Example:
INPUT:
n=2
1. string: stack
2. string: exchange
OUTPUT:
No sorted strings

I am having a problem with accessing some variables in the following program:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 30

char** read(int *pn);
int isSorted(char *str);
char** sortedArr(char **str,int pn);

char** read(int *pn)
{
   do
   {
       printf("n = ");
       scanf("%d",pn);
   }
   while(*pn < 1);

   char **arr;
   arr=(char **)malloc(*pn * sizeof(char *));
   int i;
   for(i=0; i<*pn;i++)
       arr[i]=(char *)malloc(MAX+1);

   for(i=0; i<*pn; i++)
   {
       printf("%d. word: ",i+1);
       scanf("%s",arr[i]);
   }

   return arr;
}

int isSorted(char *str)
{
    int i,j;
    for(i=0; i<strlen(str)-1;i++)
        for(j=i+1; j<strlen(str); j++)
            if(strcmp(str+i,str+j) > 0)
                return 1;
    return 0;
}

char** sortedArr(char **str,int pn)
{
    char **sortArr;
    sortArr=(char **)malloc(pn * sizeof(char *));

    int i;
    for(i=0; i<pn; i++)
    {
        if(isSorted(sortArr[i]))
            sortArr[i]=(char *)malloc(MAX+1);
    }

    for(i=0; i<pn; i++)
    {
        if(isSorted(sortArr[i]))
            printf("%d. word: %s",sortArr+i);
    }

    return sortArr;
}

int main()
{
    char **arr;
    char **sortArr;
    int pn;

    arr=read(&pn);

    sortArr=sortedArr(arr,pn);

    int i;
    for(i=0; i<pn; i++)
        free(arr[i]);
    free(arr);
    return 0;
}

The problem is how to access the variable pn in function sortedArr()?

9
  • 3
    If you want to access pn in sortedArr, you need to pass it as an argument. Commented Mar 2, 2016 at 16:22
  • Do you want the words sorted or to print out which of the words in the array have all character sorted? Like: "abcd" is sorted, print it, no matter if it is before or after the word "rst". Commented Mar 2, 2016 at 18:58
  • @Bob__ Output should be all strings that are sorted lexicographically, no matter where they appear. Example: 1. "abcd" 2. "rst" 3. "stack" Output is 1. "abcd" 2. "rst". It doesn't matter if "rst" is first and "abcd" is second Commented Mar 2, 2016 at 19:09
  • in C, when calling any of the memory allocation functions: (malloc, calloc, realloc) do not cast the returned value. the returned value has type void* so can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, maintain. 2) always check (!=NULL) the returned value to assure the operation was successful. Commented Mar 3, 2016 at 20:03
  • when calling any of the scanf() family of functions, 1) always check the returned value (not the parameter value) to assure the operation was successful. 2) when using the '%s" format specifier, always include a max length modifier. Otherwise the user can overrun the input buffer (which is undefined behaviour and can lead to a seg fault event) 3) the max length modifier must be 1 less that the length of the input buffer because the '%s' format specifier will always append a NUL byte. Commented Mar 3, 2016 at 20:22

3 Answers 3

1

Your program has several issues, I'll try to address the most part of them.

Let's start from the isSorted function, if your goal is to check if a word has all the character sorted, you don't need to use strcmp or a nested loop. All what you need is something like this:

// check if  all the character in the string are sorted
int isSorted( char *str )
{
    int i, length = strlen(str) - 1;

    for ( i=0; i < length; i++ )
        if ( str[i] > str[i+1] )
            return 0;
    return 1;
}

I'll separate the printing function from the input one and copy ones, so to have:

void print( char **str, int n )
{
    int i;
    for ( i = 0; i < n; i++ ) {
        printf("word %d: %s\n", i + 1, str[i]);
    }
}

There are several methods to read some words from stdin and some are presented in other answers. I'll use this, but you should check all the pointers returned from malloc:

char** read(int *pn)
{
    char buffer[BUFSIZE];

    printf("Please, enter number of words: ");
    while ( *pn < 1  && fgets(buffer, BUFSIZE, stdin) ) {
        sscanf(buffer, "%d", pn);
    }

    int i = 0, length;
    printf("\nPlease, enter the words: \n");
    char **arr = malloc(*pn * sizeof(char *));
    while ( i < *pn ) {
        // read words one line at a time
        if ( !fgets(buffer, BUFSIZE, stdin) )
            break;

        length = strlen(buffer);
        // ignore empty lines
        if ( length < 2 )
            continue;

        arr[i] = malloc(length);
        memcpy(arr[i],buffer,length);
        // add the null terminator
        arr[i][length - 1] = '\0';

        ++i;
    }
    *pn = i;
    return arr;
}

Then, I'm not really sure if you want only to print the words that are "sorted" or you actually need to copy them (and print later). I'll show you the latter:

// return an array of string containing only the sorted ones
char** sortedArr( char **str, int n, int *m)
{
    char **sortArr = NULL;
    char *sorted = calloc(n,1);
    int i;
    *m = 0;
    // first find (and count) the sorted strings
    for ( i = 0; i < n; i++ ) {
        if ( isSorted(str[i]) ) {
            // maybe you need only to print str[i] now...
            sorted[i] = 1;
            ++(*m);
        }
    }
    // then copy the sorted. I'm not sure if you need this
    int j = 0, length = 0;
    if ( *m ) {
        sortArr = malloc(*m * sizeof(char *));
        for ( i = 0; i < n; i++ ) {
            if ( !sorted[i] )
                continue;
            length = strlen(str[i]) + 1;
            sortArr[j] = malloc(length);
            memcpy(sortArr[j],str[i],length);
        }
    }
    free(sorted);
    return sortArr;
}

Finally the main function (and the one to free allocated memory):

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

#define BUFSIZE 128

char** read( int *pn );
int isSorted( char *str );
char** sortedArr( char **str, int n, int *m );
void print( char **str, int n );
void freeArr( char **str, int n );

void freeArr( char **str, int n )
{
    if ( !str ) return;
    int i;
    for ( i = 0; i < n; i++ ) {
        free(str[i]);
    }
    free(str);
}

int main()
{
    char **arr = NULL;
    char **sortArr = NULL;
    int n = 0, m = 0;

    arr = read(&n);
    print(arr, n);
    printf("\nSorted: \n");
    sortArr = sortedArr(arr,n,&m);
    print(sortArr, m);

    freeArr(sortArr, m);
    freeArr(arr, n);
    return 0;
}

Hope it helped. Tell me if there is something wrong or I have misunderstood your task.

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

Comments

1

Change the sortedArr function, so that it takes pn as an argument: char** sortedArr(char **str, int pn)...

Then, in your main(), change the sortArr=sortedArr(arr) to sortArr=sortedArr(arr, pn), to pass the value to the function. Then you can use the pn value inside the sortedArr function.

2 Comments

The program crashes in function sortedArr(). Could you point out what is the possible problem? I have edited the post.
Notice this: in sortedArr function, you are doing: if(isSorted(sortArr[i])), before sortArr[i] has been initialized, which you do on the next line...sortArr[i]=(char *)malloc(MAX+1);. So you are getting an invalid pointer access.
1

One head-scratcher is:

for(i=0; i<*pn;i++)
    arr[i]=(char *)malloc(strlen(arr+i)+1);

for(i=0; i<*pn; i++)
{
    printf("%d. word: ",i+1);
    scanf("%s",arr[i]);
}

You should combine both because you cannot allocate properly until you know the length of word. Allocating some strlen(arr+i)+1 for who knows what is at arr+i is meaningless (and unless arr+i just happens to be a nul-terminated string, undefined behavior). When you are taking input, you can/should use a temprory buffer to hold the input until you can validate it is what you want it to be. At minimum, test the return to scanf to insure you actually had a sucessful conversion to the type of input you are expecting. For instance:

for (i = 0; i < *pn; i++)
{
    char buf[64] = "";          /* however long is sufficient for word */

    printf ("%d. word: ",i+1);

    if (scanf ("%63[^\n']%*c",buf) == 1)
        arr[i] = strdup (buf);  /* allocates/copies buf for arr[i]  */
    else {
        fprintf (stderr, "error: invalid input for word[%d].\n", i);
    }
}

(note: you have similar allocation issues in sortedArr)

For your sort, you need to pass pn as a parameter to sortedArr so you at least know how many strings you are dealing with. Rather than looping and attempting to compare adjacent elements, why not have your sortedArr function simply call qsort and sort the array you create in read. If you want to preserve both the original and sorted arrays, then have sortedArr call memcpy and create a copy of arr and then call qsort on the copy. All you need to do is create a string comparison compare function for qsort to compare arr or the copy, e.g.:

/* comparison function for qsort (char **) */
int cmp_str (const void *a, const void *b)
{
    /*  The actual arguments to this function are "pointers to
        pointers to char", but strcmp(3) arguments are "pointers
        to char", hence the following cast plus dereference 

        note: to reverse sort order, swap a and b, below  */

    return strcmp (* (char * const *) a, * (char * const *) b);
}

Which you can then use to sort your arr (or copy) with

qsort (arr, pn, sizeof *arr, cmp_str);

Much less error-prone then a one-off loop and index attempt. Give it a try and let me know if you have questions.

1 Comment

Nice, but it seems the OP doesn't want to sort all the words, he (she) only needs to find out the words with characters alphabetically sorted.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.