Search code examples
c99getline

reading a line of input c


I'm trying to write a C99 program (without GNU extensions if at all possible) need to take input from the user of arbitrary length. Here is what I have so far and it keeps SEGFAULT-ing on line 26.

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

int get_line(char* buffer, FILE* fp) {
    int bufread = 0;
    int bufsize = LINE_MAX;
    if(buffer == NULL) {
        buffer = (char*)malloc(sizeof(char)*bufsize);
        strcpy(buffer, "");
    }

    while(true) {
        if(bufread == bufsize) {
            bufsize += LINE_MAX;
            buffer = (char*)realloc(buffer, sizeof(char)*bufsize);
        }

        char character = fgetc(fp);

        if(character == '\n') {
            break;
        } else {
            strcat(buffer, character);
            bufread += 1;
        }
    }

    return bufread;
}

int main() {
    printf("Enter a string: ");
    char* string = NULL;
    int chars_read = get_line(string, stdin);
    printf("The number of chars read is %d\n", chars_read);
    printf("The string you entered was %s\n", string);
    return 0;
}

Solution

  • In get_line

       buffer = (char*)malloc(sizeof(char)*bufsize);
       strcpy(buffer, "");
    
    • to do a strcpy to just set the first character to 0 is a complicated way, just do *buffer = 0;.

    • sizeof(char) is 1 by definition

    • you do not need to do the cast

    Note also that if buffer is not given NULL in argument the behavior is dangerous and can be undefined because the code supposes the given buffer has at least LINE_MAX character.

    In

      char character = fgetc(fp);
    

    you cannot detect the EOF, need to be an int

    In

       if(character == '\n') {
           break;
    

    You missed to detect the EOF

    You missed to add the null character at the end of buffer here of after the while

    In

    strcat(buffer, character);
    

    this is invalid because the second argument of strcat must be a char*, strcat concatenate two strings, not a string with a character

    Supposing there is a strcat to append a character that way to do will each time search for the end of the string to append the character, just use bufread gicing the index

    Because the profile of the function is

    int get_line(char* buffer, FILE* fp) {
    

    when you go out of get_line you lost the current value of buffer, creating a memory leak, and in main printf("The string you entered was %s\n", string); printf the null pointer. To avoid that you can give the string as an output parameter, allowing you to still return the new length :


    A proposal :

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>
    #include <stdbool.h>
    
    size_t get_line(char ** buffer, FILE* fp) {
      size_t bufsize = LINE_MAX;
      size_t bufread = 0;
    
      *buffer = malloc(bufsize);
    
      if (*buffer == NULL)
        return 0;
    
      while (true) {
        if (bufread == bufsize) {
          bufsize += LINE_MAX;
          *buffer = realloc(*buffer, bufsize);
        }
    
        int character = fgetc(fp);
    
        if ((character == '\n') || (character == EOF)) {
          (*buffer)[bufread] = 0;
          /* note you can do : *buffer = realloc(*buffer, bufread + 1); to only use the needed memory */
          return bufread;
        }
    
        (*buffer)[bufread++] = character;
      }
    }
    
    int main() {
      printf("Enter a string: ");
    
      char * string = NULL;
      size_t chars_read = get_line(&string, stdin);
    
      if (string != NULL) {
        printf("The number of chars read is %zu\n", chars_read);
        printf("The string you entered was '%s'\n", string);
        free(string);
      }
    }
    

    Compilation and execution :

    pi@raspberrypi:/tmp $ gcc -pedantic -Wextra p.c
    pi@raspberrypi:/tmp $ ./a.out
    Enter a string: is it working ?
    The number of chars read is 15
    The string you entered was 'is it working ?'
    pi@raspberrypi:/tmp $ ./a.out
    Enter a string: 
    The number of chars read is 0
    The string you entered was ''
    

    And under valgrind :

    pi@raspberrypi:/tmp $ valgrind ./a.out
    ==20382== Memcheck, a memory error detector
    ==20382== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==20382== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
    ==20382== Command: ./a.out
    ==20382== 
    Enter a string: what about memory leak ?
    The number of chars read is 24
    The string you entered was 'what about memory leak ?'
    ==20382== 
    ==20382== HEAP SUMMARY:
    ==20382==     in use at exit: 0 bytes in 0 blocks
    ==20382==   total heap usage: 3 allocs, 3 frees, 4,096 bytes allocated
    ==20382== 
    ==20382== All heap blocks were freed -- no leaks are possible
    ==20382== 
    ==20382== For counts of detected and suppressed errors, rerun with: -v
    ==20382== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)