Floating-Point data input in Bash and C I wrote a function that dynamically stores statistical data. I tried to clone the functionality of how I had originally written my solution in Bash. Is there perhaps something I could improve, or do better?

Bash Solution

#!/bin/bash

data_set=()
while IFS= read -r -p 'Enter statistical data (empty line to quit):' input; do
  [[ $input ]] || break
  data_set+=("$input")
done


C Solution

double* get_data(int* data_size)
{
    double input;
    char line[4096];
    double* data_set = NULL;
    double* new_data = NULL;
    int size = 0;

    puts("Enter data (end w/empty line): ");
    while(fgets(line, sizeof(line), stdin))
    {
        if(line[0] == '\n') break;

        new_data = realloc(data_set, (size+1) * sizeof(double));

        if(sscanf(line, "%lf", &amp;input))
        {
            if(new_data == NULL)
            {
                free(data_set);
                puts("Error (re)allocating memory");
                exit(1);
            }
           data_set = new_data;
           data_set[size++] = input;
       }
    }

    *data_size = size;

    return data_set;
}
From your bash script, I assume that you expect a proper (no junk) input. In that case, you can just do

while ((c = getchar()) != '\n') {
    ungetc(c, stdin);
    scanf("%lf%*c", &amp;x);
    printf("x = %g\n", x);
}


Of course, you'll replace printf with memory (re)allocation.

If you stick to your solution, I think that

if (sscanf(line, "%lf", &amp;input))
{
    new_data = realloc(data_set, (size+1) * sizeof(double));
    ...


makes a bit more sense. The way you're doing it now, you (re)allocate the memory even if the input is not a real number. This can lose you all your old data, if realloc has to move the block (so new_data != data_set) in the same step in which input is not a real number.

Edit: As requested in the comments, I'll elaborate why and how the original code could lose the data. So, I'm talking about this code:

while(fgets(line, sizeof(line), stdin))
{
    if(line[0] == '\n') break;

    new_data = realloc(data_set, (size+1) * sizeof(double));

    if(sscanf(line, "%lf", &amp;input))
    {
        if(new_data == NULL)
        {
            free(data_set);
            puts("Error (re)allocating memory");
            exit(1);
        }
       data_set = new_data;
       data_set[size++] = input;
   }
}


There are two things to consider:


The sole purpose of the line

if(sscanf(line, "%lf", &amp;input))


is to make sure that line contains a real number (and to put it in input, as a number). If we were sure that line contains a double, we wouldn't use if.
The line

new_data = realloc(data_set, (size+1) * sizeof(double));


tries to reallocate the memory pointed to by data_set so that it would fit size+1 doubles. However, this may not be possible if the memory right behind the block pointed to by data_set is occupied.

In that case, another memory block of the required size will be allocated, the data will be copied there, and its address will be returned as the return value of realloc, which we save in new_data. The old memory is freed


So, what happens if both of these things happen, i.e., if we don't input a number and the occupation of the memory is such that realloc cannot simply expand the occupied memory?

First, we do realloc:

new_data = realloc(data_set, (size+1) * sizeof(double));


Now, our memory old memory (the one pointed to by data_set) is freed, a new memory is allocated and pointed to by new_data, and the data is copied to this new memory.

But, since we didn't input the number, this if will have a false condition:

if(sscanf(line, "%lf", &amp;input))


So, we do not enter this block and, among other things, we don't do

data_set = new_data;


Hence, for the next step, our data_set points to some freed memory (where our data used to be), while the pointer new_data will be lost, either with the next new_data = realloc(...) command, or when we exit the function. In both cases, our data is unreachable, and any action on data_set will have unpredictable (most probably wrong) results.

It is not very probable that this will happen, but it might, and the solution is simply to reallocate the memory if and only if we're going to work with it. Hence, my suggestion above.