0

I want to read phrases until I input Ctrl + Z and then display them. I wrote a code, but after I input one phrase it shows the phrase and exits. Also, I want to dynamically allocate the memory.

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

int main()
{
    char words[100], 
         **phrases, 
         **aux;
    int c = 0;
    phrases = (char **)malloc(1 * sizeof(char *));
    if (phrases == NULL) exit(1);

    aux = (char **)malloc(1 * sizeof(char *));
    if (aux == NULL) exit(1);

    do {
        printf("Enter phrase: ");
        fgets(words, 100, stdin);
        aux[c] = (char *)malloc(strlen(words) * sizeof(char));
        if (aux[c] == NULL) exit(1);
        phrases[c] = aux[c];
        strcpy(phrases[c], words);
        c++;
        aux = (char **)realloc(phrases, (c + 1) * sizeof(char *));
        if (aux == NULL) exit(1);
        phrases = aux;
    } while (strcmp(phrases, "^Z") == 0);

    for (int i = 0; i < c; i++) {
            fputs(phrases[i], stdout);
            printf("\n");
    }

    for (int i = 0; i < c; i++) free (phrases[i]);
    free (phrases);
    return 0;
}

Can you tell me what I am doing wrong and how it should be done?

Student
  • 805
  • 1
  • 8
  • 11
Ghost
  • 401
  • 3
  • 15

1 Answers1

1

Trial and error is nice to learn. But at a moment, things are to be cleaned up.

First do not cast malloc in C: it is useless and can only let nasty bugs be silent.

Then you are using 2 dynamically allocated arrays of strings phrases and aux and at a moment just use:

phrases=aux;

This is bad, because it will leak the memory previously pointed to by phrases: it is still allocated, but you can neither access to it nor even free it until the end of the program.

Keep It Stupid Simple is a good rule. Here you could forget aux and only use phrases.

Anyway your real question is about how to know that a Ctrl Z was typed? Well a Ctrl-Z on Windows or Ctrl-D on Unix-like generate and end of file condition when typed on a terminal - they have no effect when read from a file or pipe...

fgets return NULL on end of file (or error) so you should test that in your loop. And do not try to find a clever while here because the read has to come after a prompt and the test has to come immediately after the read. So stick to a good old for (;;) { ... break; ...}

And last, fgets reads up to an end of line and keep it into the buffer. So there is no need to display an additional one after printing your phrases unless you want to make clear what happens with lines longer than 99 characters

After all that, you code could become:

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

int main()
{
    char words[100], **phrases /*, **aux */;
    int c=0;
    phrases=(char **)malloc(1*sizeof(char *));
    if (phrases==NULL) exit(1);
    /* aux=(char **)malloc(1*sizeof(char *));
    if (aux==NULL) exit(1); */
    for(;;) {
        printf("Enter phrase: ");
        if (NULL == fgets(words, 100, stdin)) break;  // exit loop on end of file
        phrases[c]=(char *)malloc(strlen(words)*sizeof(char)); // note sizeof(char) is 1 by definition
        if (phrases[c]==NULL) exit(1);
        strcpy(phrases[c], words);
        c++;
        phrases=(char **)realloc(phrases, (c+1)*sizeof(char *));
        if (phrases==NULL) exit(1);
    }
    printf("\n");               // skip a line here to go pass the prompt
    for (int i=0; i<c; i++) {
            fputs(phrases[i], stdout);
            //printf("\n");     // you can keep it to make long lines splitted
    }
    for (int i=0; i<c; i++) free (phrases[i]);  // ok free the strings
    free (phrases);                             // then the array
    return 0;
}

Minor improvements: sizeof(char) can be omitted because it is 1 by definition. But you can leave it if you find it more clear or simply more consistent.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks for all the clarifications. Also I removed casting malloc and stopped using aux. It works just as intended! – Ghost Dec 18 '18 at 10:56