The usual approach is to allocate some amount of space at first (large enough to cover most of your cases), then double it as necessary, using the realloc function.
An example:
#define INITIAL_ALLOCATED 32 // or enough to cover most cases
...
size_t allocated = INITIAL_ALLOCATED;
size_t size = 0;
...
int *a = malloc( sizeof *a * allocated );
if ( !a )
// panic
int val;
while ( fscanf( in, "%d", &val ) == 1 )
{
if ( size == allocated )
{
int *tmp = realloc( a, sizeof *a * allocated * 2 ); // double the size of the buffer
if ( tmp )
{
a = tmp;
allocated *= 2;
}
else
{
// realloc failed - you can treat this as a fatal error, or you
// can give the user a choice to continue with the data that's
// been read so far.
}
a[size++] = val;
}
}
We start by allocating 32 elements to a. Then we read a value from the file. If we're not at the end of the array (size is not equal to allocated), we add that value to the end of the array. If we are at the end of the array, we then double the size of it using realloc. If the realloc call succeeds, we update the allocated variable to keep track of the new size and add the value to the array. We keep going until we reach the end of the input file.
Doubling the size of the array each time we reach the limit reduces the total number of realloc calls, which can save performance if you're loading a lot of values.
Note that I assigned the result of realloc to a different variable tmp. realloc will return NULL if it cannot extend the array for any reason. If we assign that NULL value to a, we lose our reference to the memory that was allocated before, causing a memory leak.
Note also that we check the result of fscanf instead of calling feof, since feof won't return true until after we've already tried to read past the end of the file.