Search code examples
carraysmallocscanfrealloc

Is creating a long buffer and then strdup into a new variable the best way to save any input from scanf?


#include <stdio.h>

int main() {
    int buf[1024];
    if (scanf("%1023[^\n]", buf) == 1) {
        int *myarray = strdup(buf);     // strdup should use malloc to dynamically allocate basing on the length of string
        if (myarray) {
            printf("Value: %s\nLength of string: %d\nDynamically allocated space: %d\n", myarray, strlen(myarray), ( (strlen(myarray) + 1)*sizeof(int) ));  // the last one should be the value of the new dyn allocated "myarray" array
            free(myarray);
        }
    }
    return 0;
}

Is this the best way to avoid memorization problems with scanf?

Can I use realloc in some way instead of using strdup and buf?

Does the part (strlen(myarray) + 1)*sizeof(int) correctly prints the size in bytes of the dynamically allocated myarray with strdup?


Solution

  • As I documented in this other question, the code in the question is a quick and dirty way to get a line of input from the user, but it has some limitations:

    • it fails if the stream has an empty line
    • long lines will be silently split in 1023 byte chunks.
    • it leaves the newline pending in the input stream.

    getline is available for this on POSIX systems, but it stores the newline at the end of the array if one was present in the stream.

    Here is a simple implementation of my_getline() that behaves similar to getline but does not keep the newline:

    #include <stdio.h>
    #include <stdlib.h>
    
    int my_getline(char **lineptr, size_t *n, FILE *stream) {
        char *ptr = *lineptr;
        size_t size = *n;
        size_t pos = 0;
        int c;
        while ((c = getc(stream) && c != '\n') {
            if (pos + 1 >= size) {
                /* reallocate the array increasing size by the golden ratio */
                size = size + (size / 2) + (size / 8) + 16;
                ptr = realloc(ptr);
                if (ptr == NULL) {
                    ungetc(c, stream);
                    return EOF;
                }
                *n = size;
                *lineptr = ptr;
            }
            ptr[pos++] = c;
            ptr[pos] = '\0';
        }
        return (int)pos;
    }
    
    int main() {
        char *mystring = NULL;  // must be initialized
        size_t size = 0;        // must be initialized
        int res;
    
        while ((res = my_getline(&mystring, &size, stdin)) >= 0) {
            printf("Value: %s\n"
                   "Length of string: %d\n",
                   "Allocated size: %d\n",
                   mystring, res, (int)size);
        }
        free(mystring);
        return 0;
    }
    

    Regarding your last question: Does the part (strlen(myarray) + 1)*sizeof(int) correctly prints the size in bytes of the dynamically allocated myarray with strdup?: No this expression is incorrect, there is no reason to multiply by sizeof(int). There is no portable way to determine the size of the block allocated by strdup() (or malloc), it is only guaranteed to have an allocated size of at least strlen(myarray) + 1 if the allocation was successful.