0

I'm making a program that reads two sets of data (float) from two different .txt files, and then it transfers these data to two different arrays, which will be used in further calculations. However, when I try to use dynamic allocation more than once, something goes wrong and the data seem not to be stored in the array.

The following simplified program seems to be working fine:

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

int main()
{
    float *VarA;
    int n = 0;
    int *counter;
    int i;
    FILE *input1;

    input1 = fopen("C:\\Users\\...test.txt","r");

    VarA = (float*)calloc(20001, sizeof(float));

    for(i = 0; i < 20001; i++)
    {
        fscanf(input1,"%f",&VarA[i]);
        printf("%f\n",VarA[i]);
    }

    free(VarA);

    fclose(input1);

    return 0;
}

it successfully shows the data stored in the array VarA. However, if I introduce a new array to count the number of lines in the file (which is necessary for my further calculations), I just get the value 0.000000 from every array element:

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

int main()
{
    float *VarA;
    int n = 0;
    int *counter;
    int i;
    FILE *input1;

    input1 = fopen("C:\\Users\\...test.txt","r");

    counter = (int*)calloc(100000, sizeof(int));

    while(fscanf(input1,"%f",&counter[n]) != EOF)
    {
        n++;
    }

    free(counter);

    printf("n = %i\n", n);

    VarA = (float*)calloc(n, sizeof(float));

    for(i = 0; i < n; i++)
    {
        fscanf(input1,"%f",&VarA[i]);
        printf("%f\n",VarA[i]);
    }

    free(VarA);

    fclose(input1);

    return 0;
}

I know that I can avoid using another array to count the number of lines. The point is that every time I use another array, for any purpose, I get the same result. For instance, if I don't use an array to count the number of lines, but I make another one to store my other set of data, one of these arrays just won't present the data after the reading. I tried to modify my program several times in order to find the source of such behavior, but without success.

  • 6
    `while(fscanf(input1,"%f",&counter[n]) != EOF)` - you specify `%f` while reading into `int`. This may not only result in wrong value stored, but also in undefined behavior in some cases. – yeputons Feb 16 '17 at 02:31
  • 2
    Possible duplicate of [Resetting pointer to the start of file](http://stackoverflow.com/questions/32366665/resetting-pointer-to-the-start-of-file) – Ken Y-N Feb 16 '17 at 02:34
  • Note there is no need for the first `calloc()` as you can just read into a dummy variable, nor should you cast the result. – Ken Y-N Feb 16 '17 at 02:37
  • 3
    You don't check return value of scanf in the second code. And you should check for `== 1`, not `!= EOF`. – M.M Feb 16 '17 at 02:37
  • Thank you people. I just needed to reset the pointer by using rewind(input1); The int-float issue was (clearly) a mistake, but it wasn't the origin of the main problem I was facing. However, another odd problem remains. When I modify the program to read another file, in the same way it does to the first file, it doesn't print the second set of data, only the first one. But if I delete the part saying it to print the first set, the second set is printed. Any idea on what's happening? Should I create another question? Modify this one? – user3277482 Feb 16 '17 at 12:33

2 Answers2

4

(At least) two major problems: first,

counter = (int*)calloc(100000, sizeof(int));
while(fscanf(input1,"%f",&counter[n]) != EOF) {
    n++;
}
free(counter);

is basically saying "Grab me a chunk of memory, fill it with data as I read the file, then throw it away without ever using it." Probably not what you intended. Then,

VarA = (float*)calloc(n, sizeof(float));
for (i = 0; i < n; i++) {
    fscanf(input1,"%f",&VarA[n]);
    printf("%f\n",VarA[n]);
}
free(VarA);

which says, "Grab a big chunk of memory, then read data from after the end of the file I just read everything from, put it there, then throw it away."

If you want to read the data from the same file again, you'll have to close it an reopen it (or "seek" to the start). And if you want to do anything with it, you'll have to do it before free()ing the memory you loaded it into.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
  • Thank you Lee. Despite the int-float mistake, the main problem was that I wasn't using rewind(input1). I need to count the number of lines in order to know the size of the array I will allocate (each file line has only one float number). I solved it by using: while(fscanf(input1,"%f") == 1) n++; – user3277482 Feb 16 '17 at 12:40
  • A common way that's done in libraries (but probably too complicated for a student) is to start by allocating a small number (say 16) pointers in the array, then start reading the file. Whenever the array fills up, `realloc` it to 3/2 its current size, copy over the pointers and continue. This ensures the number of allocations will be reasonably small (O(log(n)), you only read the file once, and all it costs is a bit of wasted memory. That's how Java dynamic arrays are done, for example. – Lee Daniel Crocker Feb 16 '17 at 22:38
3
counter = (int*)calloc(100000, sizeof(int));
         // ^--- `int*`                ^--- `int`
                          // v--- `int` pointer
while(fscanf(input1,"%f",&counter[n]) != EOF)
                   // ^--- `float` designator

Do you see any discrepancies here? Your code allocates ints, then passes a pointer to those ints to fscanf telling it they're floats (using the %f designator). According to the C standard draft n1570, section 7.21.6.2p10 this constitutes undefined behaviour:

If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

My suggestion would be to use the * assignment suppression modifier here, for example:

while (fscanf(input1, "%*f") != EOF) n++;

or, alternatively

while (fscanf(input1, "%f", &(float){0}) != 1) n++;

Note also how I've changed the check from EOF to 1. You can find more information about the return values of fscanf here (which you really should read before using any scanf-related function... and stop guessing, because guessing in C can be harmful).

Additionally, you need to rewind your file once it reaches EOF, otherwise every call to fscanf following this loop will return EOF:

rewind(input1);

P.S. Don't cast malloc in C. This goes for calloc and realloc, too. There's a lot of this quoted stuff that has opengroup manuals of its own; I'll leave it as an exercise to you to find (and read) the opengroup manuals.

autistic
  • 1
  • 3
  • 35
  • 80
  • Thank you for your reply. It helped me a lot, because now I know that I don't need to create another variable in order to count the number of lines. And you're right, I needed to use rewind. Now the program is running properly. However, there two problems remaining. Firstly, it didn't work: while (fscanf(input1, "%*f") != 1) n++; However, it worked: while (fscanf(input1, "%f") == 1) n++; The other thing is: When I try to load another file, in the same way I did to the first one, I can print the data from only one of the arrays, not from both. I don't know what's going on. – user3277482 Feb 16 '17 at 12:22
  • @user3277482 Well, I shouldn't have expected `fscanf(input1, "%*f")` to ever return 1... the correct code there is `while (fscanf(input1, "%*f") != EOF) n++;`... You most certainly would want the assignment suppressing `*`, or else you'll want to use something more like `while (fscanf(input1, "%f", &(float){0}) == 1) n++;`... you most certainly *do not want `fscanf` to provide too few arguments to `fscanf`*, though... – autistic Jul 03 '18 at 22:50
  • I also feel the need to point out that you probably *shouldn't* be duplicating data like that. We're taught that we should avoid making copies, thus you're likely better off rethinking the representation you've used on the disk. If you had something more *fixed size* (such as a fixed sized, serialised IEEE754 representation), you could just take any index `n`, multiply that by the fixed size and seek straight to the position on disk... allowing you to potentially support multi-terabyte datasets if you're using the right filesystem... – autistic Jul 03 '18 at 23:20