Here's my code. No matter what I enter for the weight and height, it always outputs 0.0000000. I'm not sure what is wrong with it.
#include <stdio.h>
#include <math.h>
double bmi(w,h){
double bmi = w/h;
return bmi;
}
int main() {
double height;
double weight;
printf("Enter your height in meters: ");
scanf("%lf", &height);
height = pow(height, 2);
printf("Enter your weight in kilograms: ");
scanf("%lf", &weight);
printf("Your BMI is %f\n", bmi(weight, height));
return 0;
}
CodePudding user response:
You're using an old-style function definition by not specifying the types of the arguments. This results in the arguments having type int
, which in turn means you're performing integer division.
This is actually undefined behavior. Because the function doesn't specify the types of the parameters, the function definition does not also specify a prototype for the function.
Section 6.9.1p7 of the C standard regarding function definitions states:
The declarator in a function definition specifies the name of the function being defined and the identifiers of its parameters. If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit. If the declarator includes an identifier list, 163) the types of the parameters shall be declared in a following declaration list. In either case, the type of each parameter is adjusted as described in 6.7.6.3 for a parameter type list; the resulting type shall be a complete object type.
And the definition in question has an identifier list, not a parameter type list.
This means the only conversions that happen on arguments are the default argument promotions, which is only integer promotions and conversion of float
to double
. The result is that the types of the parameters being passed (i.e. double
) are not compatible with the expected parameter types (i.e. int
), triggering undefined behavior.
This is spelled out in section 6.5.2.2p6 regarding function calls:
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type
float
are promoted todouble
. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis ( , ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases: ... [ not applicable]
You need to explicitly set the types of the parameters:
double bmi(double w, double h){
double bmi = w/h;
return bmi;
}
CodePudding user response:
Make double bmi(w,h)
to double bmi(double w, double h)
. Without explicit declaration of argument type, it defaults to int
.
warning: type of ‘w’ defaults to ‘int’ [-Wimplicit-int]
5 | double bmi(w,h){
| ^~~
So, with relevant type:
double bmi (double w, double h) {
return (w / h);
}
Always turn on warning from compiler. If you're on a linux OS use an alias. For GCC
on my Ubuntu I use something like below for test-code:
alias gcdev='gcc -Wall -Wextra -pedantic -g3 -O2 -fsanitize=address,undefined -std=c17 -march=native -I./include'
CodePudding user response:
If read the compiler warnings carefully says
warning: type of 'w' defaults to 'int' [-Wimplicit-int]
1 | double bmi(w,h){
| ^~~
<source>:1:8: warning: type of 'h' defaults to 'int' [-Wimplicit-int]
So, if you declare the data type of w
and h
as doubles
, then it will be fine.
This is a kind of old-style function and can be turned on by using -Wold-style-definition
flags when compiling.
Now, you will say that you are creating a double
variable bmi
in your function bmi
, so it should return a proper value. But it doesn't because any operation between two int
s will always return an int
.
Use -std=c17
flags to keep yourself upto-date with C standards.
And that is UB
Undefined Behaviour.
Main context from cppreference.com
about UB:
undefined behavior - there are no restrictions on the behavior of the program. Examples of undefined behavior are memory accesses outside of array bounds, signed integer overflow, null pointer dereference, modification of the same scalar more than once in an expression without sequence points, access to an object through a pointer of a different type, etc. Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful.
The compilers are required to issue diagnostic messages (either errors or warnings) for any programs that violates any C syntax rule or semantic constraint, even if its behavior is specified as undefined or implementation-defined or if the compiler provides a language extension that allows it to accept such program. Diagnostics for undefined behavior are not otherwise required.
Correct C programs are free of undefined behavior, compilers may produce unexpected results when a program that actually has UB is compiled with optimization enabled:
I also noticed that your variable and function name are same, which is also a problem. So, use any other name like val
or bmi_val
.
You are not checking the result of scanf()
function, it checks for any sort of bad format.
Your function should be TRY IT ONLINE
:
double bmi(double w, double h)
{
return w / h;
}
Recommeded GCC
flags for warnings:
-g -W -Wall -Wextra -Wuninitialized -Wstrict-aliasing -ggdb3 -std=c17 -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wshadow