I need the array in the main function to pass it's values the the average function.
The recommended, future-proof way is
double average(const double *const values, const size_t count);or
double average(const double values[], const size_t count);which happen to be
equivalent.
In standard hosted C environments (meaning, on computers on OSes like Windows, Linux, or Mac OS),
double is the recommended floating-point type, and is usually implemented as a
IEEE 754-2008 Binary64; whereas
float is usually
IEEE 754-2008 Binary32, and gets promoted to
double in many situations. (See e.g.
C11 final public draft n1570, 6.3.1.8p2: "The values of floating operands and of the results of floating expressions may be represented in greater range and precision than that required by the type; the types are not changed thereby", except for explicit casts as noted in a footnote.)
Except for
sizeof,
_Alignof, and address-of
& operators, and string literals used to initialize an array, array names decay to a pointer to its first element (n1570, 6.3.2.1p3). This means that if you have
double numbers[10]; , you can supply
numbers to anything that expects either an array of doubles, or a pointer to double, because the two are equivalent (except for the four exceptions listed above).
Because C only knows array sizes/boundaries at compile time, and only within the scope the array was declared in, we need to pass the size of the array, or rather the number of elements we want to calculate the average of, to the average function. The type used for such counts is
size_t , not
int or
long. (In 64-bit Linux, Windows, and Mac OS,
int is 32-bit, but
size_t is 64-bit. In other words, if you use
int, which is signed, on these operating systems you limit your array size to 2
31-1 = 2,147,483,647 items maximum. Sometimes that is acceptable, other times not. You cannot declare that large an array statically, but you can allocate an array of that size, or much larger, dynamically using
malloc().)
The
const keyword means "the programmer promises not to modify the value in this scope". It used to help compilers generate better code, but nowadays most C compilers can infer such promises directly from the code. Yet, these promises are very useful for us
humans, because it expresses our
intent: what we want the code to accomplish.
(A side note: Try to write comments that explain your idea, your reasoning, as to what the code should accomplish. For example, just before the implementation of the
average() function, you could add a comment block that says "Calculate the arithmetic mean of the 'count' values in 'numbers' array." Never, ever write comments that tell what the code does; always tell what the reason is that this code exists. After decades of being a professional programmer, I still cannot express how much more important this kind of commenting skill is, than being able to optimize code or write tricky code. It is a skill worth more than any IOCCC win or anything like that.)
When you interpret more complex type definitions, split them at the asterisks (reading it as
"is a pointer to"), and read the types from right to left.
So, for example,
const double value[]; reads as
"value is an array of doubles that won't be modified in this scope". In other words, the code can access the array members to see what value each member has, but promises not to try and modify any of the values therein.
Or, for example,
const double *const value; reads as
"value won't be modified in this scope, and is a pointer to double(s) that won't be modified in this scope".
Here, we promise that we won't try to change where
value points to (the right-side
const), and we also won't try to modify the values there (the left-side
const). Simples; plus, it always works, because that's how the C language types work.
(The only "exception", or rather variant, is function pointers, but there we use parentheses () to separate the function pointer part and the function argument parts, with the function return type part leftmost without any parentheses usually. So, the rule actually works even for these, there's just additional function-pointer specific stuff added on top.)
Because the count is of type size_t, the way I'd implement the averaging function is
double average(const double numbers[], const size_t count)
{
double sum = 0.0;
size_t i;
for (i = 0; i < count; i++) {
sum += numbers[i];
}
return sum / (double)count;
}
Here, the
(double)count is a
cast, converting the value of
count to
double type. It is not strictly needed, because C arithmetic promotion rules (n1570 6.3.1.8) automatically promotes it to
double because
sum is a double. It is arguable whether you should or should not use it here; I only kept it here so you could see what one looks like. They're very commonly used in such cases.
(For example, let's say you have
int n; and
int d; and you want to calculate their ratio into
double r;. If you use
r = n / d;, the division is integer division, and
r will be the integer division result (i.e., an integer) converted to
double. But, if you use
r = (double)n / d; or
r = n / (double)d; or
r = (double)n / (double)d; , all three being exactly equivalent in this case, you'll get the best approximation of the ratio a double-precision floating point number can describe.)
The function itself does not print anything, it just calculates the arithmetic mean (average), and returns it as a
double. This is recommended, because then you keep functionality in logically separated units, and can test the function (for example in a separate program) with known data to see if it works correct.
Typical bugs are off-by-one kind, like
for (i=1; i < n; i++) and
for (i=0; i<=n; i++) . Verifying with known data can help catch these.
In the main program, you'd have for example
double mynums[] = { 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1 };
double avg;
avg = average(mynums, sizeof mynums / sizeof mynums[0]);
printf("%.1f\n", avg);
Here, we use the "number of elements in an array declared in the current scope" trick.
sizeof mynums is the size of the entire array in bytes (chars), and
sizeof mynums[0] is the size of the first element in bytes (chars), so
sizeof mynums / sizeof mynums[0] is the number of elements in the array.
This does not work in a function, because the array declaration is in the function parameter. This only works as a shorthand in the same scope (function or block) where the actual array was defined in.
For example, if you have
int example(int array[15]), inside the function definition
sizeof array / sizeof array[0] will always evaluate to 15, regardless of what was passed to the function. And, if you use
int example(int array[]), trying to use
sizeof array inside the function will make your compiler complain or error. (With GCC, it'll return the size of a pointer to an int.) Simply put, there is no shortcut. If you pass an array as a parameter, you should always also pass the size of that array, too.
Okay, so you managed to read this far. Do I expect you to understand all that at once? No, not really; a lot of it is something that being vaguely aware of when learning the details will keep you on the right track, and will usually help things "click in place" well when you do learn them. All this is meant as a "oh, so
that is what that Nominal Animal was babbling about;
now it makes sense!" type of thing, and involves the core issues on the "hard stuff" I've seen when trying to help others learn C.