0

(C Programming) I am wondering how I should pass an array through a function whose size is not known until compile time? In the main, my program receives numbers(doubles) from the user and stores all numbers into an array using scanf(), except for 0. anytime a 0 is entered, it should be discarded(not stored in any way) and the user should be able to continue. The number of input from the user is unknown, but there will be no more than 1 million(10^6) number of inputs or until the user enters EOF(ctrl d). (The user is NOT asked to enter the size of the array, only the values). I then must pass the array through a seperate function that will determine the sum of the array. I have a function count() that counts the numbers in the array and that seems to work but my function sums() doesn't seem to be accessing the elements of the array because it returns 0 every time. I know I probably need a pointer but I am unsure of how to go about this. My program so far is below.

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

#define MAX_SIZE 10*10*10*10*10*10

int main();
int counts(double array[]);
double sums(double array[]);
double maxFind(double array[]);


int main()
{
double arr[MAX_SIZE];
int countNums, i;
double sum, max;

printf("Please enter values\n");

while(scanf("%f", &arr[i]) != EOF && i < MAX_SIZE)
{
    if(arr[i] == 0){
        continue;
    }
    i++;
}




countNums = counts(arr);
printf("The count of numbers read from the input is %9d\n", countNums);

sum = sums(arr);
printf("The sum of these numbers is %20.10f\n", sum);

max = maxFind(arr);
printf("The max number is %20.10f\n", max);
}




int counts(double array[])
{
int countNum = 0;
    for(int i = 0; i < sizeof(array) ; i++)
{
    if(array[i] == 0){
        continue;
    }
       countNum++;
}
return countNum;
}



double sums(double array[])
{
int i;
double sumNum = 0;
for(i = 0; i < sizeof(array); i++){
    sumNum = sumNum + array[i];
}
return sumNum;
}



double maxFind(double array[])
{
double maxNum = array[0];
for(int i = 1; i < sizeof(array); i++){
    if(array[i] > array[i-1]){
        maxNum = array[i];
    }
}
return maxNum;
}
1
  • This is a job for malloc and realloc. Make arr a pointer to double (e.g. double *arr;), allocate some initial number of doubles (e.g. size_t allocated = 2;) and then array arr = malloc (allocated * sizeof *arr); (validate the allocation), then start adding doubles to arr, keep a counter used, when used == allocated, double the number allocated with, void *tmp = realloc (arr, 2 * allocated * sizeof *arr); now validate tmp, and if good, arr = tmp; and used *= 2; and keep going, rinse and repeat. Commented Nov 5, 2019 at 3:19

1 Answer 1

1

You are faced with the classic case of "How do I read an unknown number of X?". The answer is simply allocate some initial number of elements with malloc (or calloc or realloc) and keep track of what you have allocated and then the number of elements used (filled). When used == allocated, you realloc the array and increase the number of elements available by some amount, either some fixed amount each time or some multiple of the current. Doubling the number of elements is a fairly good balance of allocation growth and the number of reallocations required.

A simple example reading an unknown number of doubles with scanf and then outputting the number read and the sum could be:

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

int main (void) {

    size_t used = 0, allocated = 2; /* used doubles/allocated doubles */
    double *arr, sum = 0.0;         /* array & sum of elements */

    /* allocate initial 'allocated' number of doubles */
    if (!(arr = malloc (allocated * sizeof *arr))) {
        perror ("malloc-arr");
        return 1;
    }

    while (scanf ("%lf", &arr[used]) == 1) {    /* read doubles until EOF */
        sum += arr[used++];                     /* add to sum, increment used */
        if (used == allocated) {                /* if used == allocated */
            /* reallocate more doubles using a temporary pointer */
            void *tmp = realloc (arr, 2 * allocated * sizeof *arr);
            if (!tmp) {                         /* validate reallocation */
                perror ("realloc-tmp");
                break;      /* don't exit, original arr still good, break */
            }
            arr = tmp;          /* assign reallocated block to arr */
            allocated *= 2;     /* update number allocated */
        }
    }
    printf ("%zu doubles entered with sum: %.2f\n", used, sum);

    free (arr);     /* free the memory you have allocated */
}

(note: you ALWAYS realloc to a temporary pointer because if realloc fails, it will return NULL overwriting your original pointer address preventing you from ever being able to access or free() the original block resulting in a *memory leak*. Ifrealloc` does fail, by using a temporary pointer, your existing data is preserved through the original pointer)

You can now pass arr and the number of elements it holds (used) to any function that needs that information.

Example Use/Output

$ ./bin/sumdoubles
1.1
2.2
3.3
4.4
5.5
6.6
7.7
8.8
9.9
9 doubles entered with sum: 49.50

or redirect 10,000 floating-point values from a file:

$ ./bin/sumdoubles < ../dat/10000float.txt
10000 doubles entered with sum: 117991733.64

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/sumdoubles
==6316== Memcheck, a memory error detector
==6316== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6316== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6316== Command: ./bin/sumdoubles
==6316==
1.1
2.2
3.3
4.4
5.5
6.6
7.7
8.8
9.9
9 doubles entered with sum: 49.50
==6316==
==6316== HEAP SUMMARY:
==6316==     in use at exit: 0 bytes in 0 blocks
==6316==   total heap usage: 6 allocs, 6 frees, 2,288 bytes allocated
==6316==
==6316== All heap blocks were freed -- no leaks are possible
==6316==
==6316== For counts of detected and suppressed errors, rerun with: -v
==6316== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Edit - Additional Problems In Your Functions

In

for(i = 0; i < sizeof(array); i++)

sizeof(array) is simply sizeof(a_pointer). An array is converted to a pointer on access. See: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). You would want to pass both the pointer and the number of elements, e.g.

double sums(double *array, size_t n)
{
    size_t i;
    double sumNum = 0;

    for(i = 0; i < n; i++){
        sumNum = sumNum + array[i];
    }

    return sumNum;
}

You would call sums in your main() as

size_t sum = sums (arr, used);

An example using a sums function (slightly modified to taste):

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

double sums (double *array, size_t n)
{
    double sumNum = 0;

    for (size_t i = 0; i < n; i++)
        sumNum = sumNum + array[i];

    return sumNum;
}

int main (void) {

    size_t used = 0, allocated = 2; /* used doubles/allocated doubles */
    double *arr, sum = 0.0;         /* array & sum of elements */

    /* allocate initial 'allocated' number of doubles */
    if (!(arr = malloc (allocated * sizeof *arr))) {
        perror ("malloc-arr");
        return 1;
    }

    while (scanf ("%lf", &arr[used]) == 1) {    /* read doubles until EOF */
        if (++used == allocated) {              /* if used == allocated */
            /* reallocate more doubles using a temporary pointer */
            void *tmp = realloc (arr, 2 * allocated * sizeof *arr);
            if (!tmp) {                     /* validate reallocation */
                perror ("realloc-tmp");
                break;  /* don't exit, original arr still good, break */
            }
            arr = tmp;          /* assign reallocated block to arr */
            allocated *= 2;     /* update number allocated */
        }
    }
    sum = sums (arr, used);     /* call functions here */

    printf ("%zu doubles entered with sum: %.2f\n", used, sum);

    free (arr);     /* free the memory you have allocated */
}

(output would be the same)

Look things over and let me know if you have any further questions.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you, this is very helpful!! but for my assignment, the sum calculation must be done by a separate function and I'm unsure of how to access the array through the function.
Sure, I just added that. You can pass arr and used to each of your functions. Just remove the sum += arr[used++]; and replace it with used++; and do your sum in your function. I left it to you to implement your functions, I'm apologize for not making that clear. My example was to show how to handle the unknown number of doubles :)
In other words you would declare your function double sums(double *array, size_t n) and get rid of sizeof(array) in your function. That is simply sizeof (a_pointer) See the addition to the answer.
I am not sure why, but my sums function is still continuing to return 0
@thornberry how does it differ from the one I posted or used in the follow-up example I added? Also, do you have full warning enable in your compile string? (e.g. add -Wall -Wextra -pedantic for gcc/clang or /W3 for VS). Then don't accept code until it compiles without warning.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.