This is like a homework from a class or so.
This is how I would do if it is for minimum changes.
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 10
float average(float *numbers)
{
float total = 0;
float avr = 0;
int i;
for(i = 0; i < MAX_N; i++) {
total = total + numbers[i];
}
avr = total / MAX_N;
return(avr);
}
int main(void)
{
float numbers[MAX_N] = {0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1};
float avr;
avr = average(numbers);
printf("The average is: %.1f\n", avr);
exit(0);
}
The most critical changes for me from the original code is the following line, since it is buffer overflow and leads security risk.
From
for(i = 0; i <= 10; i++)
To
for(i = 0; i < 10; i++)
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 (https://en.wikipedia.org/wiki/Double-precision_floating-point_format); whereas float is usually IEEE 754-2008 Binary32 (https://en.wikipedia.org/wiki/Single-precision_floating-point_format), and gets promoted to double in many situations. (See e.g. C11 final public draft n1570 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf), 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 231-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.