What is the usefulness of assert(), when we can also use printf and if-else statements will inform the user that b is 0?
#include <stdio.h>
#include <assert.h>
int main() {
int a, b;
printf("Input two integers to divide\n");
scanf("%d%d", &a, &b);
assert(b != 0);
printf("%d/%d = %.2f\n", a, b, a/(float)b);
return 0;
}
There're two things here that people conflate. Validation of run-time errors and compile-time bugs.
Avoid a runtime error with if
Say the user is supposed to enter only numbers, but the input encountered is alphanumeric then it's a run-time error; there's nothing that you, the programmer, can do about it. You've to raise this to the user in a user-friendly, graceful way; this involves bubbling up the error to the right layer, etc. There are multiple ways of doing this: returning error value, throwing an exception, etc. Not all options are available in all languages.
Catch a bug with an assert
On the other hand, there're design-time assumptions and invariants a programmer depends on when writing new code. Say you're authoring a function make_unit_vec involving division of a vector’s components by its length. Passing the 0 vector breaks this function since it’ll lead to a divide-by-zero; an invalid operation. However, you don’t want to check this every time in this function, since you're sure none of the callers will pass the 0 vector -- an assumption, not about extern user input but, about internal systems within programmer’s control.
So the caller of this function should never pass a vector with length 0. What if some other programmer doesn’t know of this assumption and breaks make_unit_vec by passing a 0 vector? To be sure this bug is caught, you put an assert i.e. you're asserting (validating) the design-time assumption you made.
When a debug build is run under the debugger, this assert will "fire" and you can check the call stack to find the erring function! However, beware that assertions don’t fire when running a release build. In our example, in a release build, the div-by-zero will happen, if the erroneous caller isn’t corrected.
This is the rationale behind assert being enabled only for debug builds i.e. when NDEBUG is not set.
Asserts are not used to inform users about anything. Assertions are used to enforce invariants in your program.
In the above program, the assert is misused. The b != 0 is not an invariant. It is a condition to be checked at runtime and the proper way to do it would be something like
if (b == 0) {
sprintf(stderr, "Can't divide by 0\n");
return -1;
}
This means that the program will check user input and then abort if it's incorrect. The assert would be inappropriate because, one, it can be compiled out using the NDEBUG macro which will, in your case, alter program logic (if the input is 0, the program will continue to the printf with the NDEBUG flag) and two because the purpose of an assert is to assert a condition at a point in the program.
The example in the wikipedia article I've linked to gives you a place where an assert is the right thing to use. It helps to ensure program correctness and improves readability when someone is trying to read and understand the algorithm.
In your program if the condition b != 0 holds true then the program execution will continue otherwise it is terminated and an error message is displayed and this is not possible with printf only. Using if-else will work too but that is not a MACRO.
assert will stop the program, it marks a condition that should never occur.
From Wikipedia,
When executed, if the expression is false (that is, compares equal to 0), assert() writes
information about the call that failed on stderr and then calls abort(). The information it > writes to stderr includes:
the source filename (the predefined macro __FILE__)
the source line number (the predefined macro __LINE__)
the source function (the predefined identifier __func__) (added in C99)
the text of expression that evaluated to 0 [1]
Example output of a program compiled with gcc on Linux:
program: program.c:5: main: Assertion `a != 1' failed.
Abort (core dumped)
It stops the program and prevents it from doing anything wrong (besides abruptly stopping).
It's like a baked-in breakpoint; the debugger will stop on it without the extra manual work of looking up the line number of the printf and setting a breakpoint. (assert will still helpfully print this information though.)
By default it's defined to be conditional on the NDEBUG macro, which is usually set up to reflect the debug/release build switch in the development environment. This prevents it from decreasing performance or causing bloat for end users.
Note that assert isn't designed to inform the user of anything; it's only for debugging output to the programmer.
That is not a good use of assert; validating user input with assert is too brutal for anything other than your own private use.
This is a better example — it is a convenient one I happen to have kicking around:
int Random_Integer(int lo, int hi)
{
int range = hi - lo + 1;
assert(range < RAND_MAX);
int max_r = RAND_MAX - (RAND_MAX % range);
int r;
while ((r = rand()) > max_r)
;
return (r % range + lo);
}
If the value of RAND_MAX is smaller than the range of integers requested, this code is not going to work, so the assertion enforces that critical constrain.
Related
Hey so I'm looking into C examples where the code will not compile due to its type error but not give any run time error when the user executes the program. I can't seem to find much about this topic. One example I found of this is: "if an expression always evaluates to true at run-time, a program containing the code if <complex test> then 42 else <type error> will be rejected as ill-typed, because a static analysis cannot determine that the else branch won't be taken 'ref = https://stackoverflow.com/questions/1517582/what-is-the-difference-between-statically-typed-and-dynamically-typed-languages'".
I'm looking into more examples of Static typing errors that aren't conditional
C is a statically typed language. So the example(s) you're seeking aren't available in C (if your program doesn't compile, there's no "runtime" - what do you execute?). However, you're misinterpreting (or misunderstanding) what that quote is trying to convey.
if an expression always evaluates to true at run-time, a program
containing the code if then 42 else will be rejected as ill-typed. because a static analysis cannot determine that the else branch won't be taken.
says code like the example below won't compile in a statically typed language like C:
#include <stdio.h>
int main(void)
{
int i = 0;
int x = 5;
if (42)
x = 0;
else
x = "str";
printf("%d\n", x);
}
(Here the if clause is always true and thus the else clause with type mismatch should never get to execute).
And that's mostly true except for "static analysis cannot determine that the else branch won't be taken.". Even if static analysers can detect that the else branch won't be taken, type checking is generally done and diagnostics emitted. So that part of the quote isn't quite accurate.
int climbStairs(int n ){
if(n==1){
return 1;
}
if (n>=2){
return (2+ climbStairs(n-2)+ climbStairs(n-1));
}
}
How do I fix the compiler error?
Your compiler is not as clever as you. You may well know that the function is never called with n less than 1, but the compiler doesn't. Personally I think it's called with n as 0 for typical inputs.
Therefore it thinks that program control can reach the function closing brace } without an explicit return, which formally is undefined behaviour if you make use of the function return value which, to repeat, I think you do.
You have this warning the compiler issues to you elevated to an error, so compilation halts.
Some fixes:
Block the recursion with the stronger if (n <= 1){.
Run-time assert before the closing brace } using assert(false) or similar.
Switch off the elevation of that warning to an error, but do deal with the warning nonetheless.
Some advice from #JonathanLeffler
Don't switch off -Werror — it is too valuable. Deal with the warning. assert(n >= 0); at the top; if (n <= 1) { return 1; } as well. Assertions fire in debug builds; erroneous parameter value handled reasonably safely and sanely even if assertions are not enabled.
And in addition to other answers here is another good trick.
If you are absolutely certain that you know more than the compiler, and there's nothing to return, then put an abort(); as the last line of your function. The compiler is smart enough to know that abort() will never return because it causes a program crash. That will silence the warning.
Note that the program crash is a good thing. Because as in this case, when the "impossible thing" does indeed happen, you get the crash and it will pop open in your debugger if you are using one.
The problem is to find the minimum number of coins owed given an amount of dollars in change, assuming that available coins to give back are 25c, 10c, 5c and 1c.
I implemented a solution with recursion in C, but somehow it kept throwing the "error: control may reach end of non-void function". I'm fairly new to C so I couldn't quite figure out whats going on.
Any help is greatly appreciated! Here's my code:
#include <cs50.h>
#include <stdio.h>
#include <math.h>
int processChange(float change){
int centsChange = round(change*100);
int arr[4] = {25,10,5,1};
for(int i=0;i<4;i++){
int numCoins =0;
int remainder = centsChange%arr[i];
if(remainder==0){
numCoins = (centsChange - remainder)/arr[i];
return numCoins;
}
if(centsChange ==1){return 1;}//base case
if(centsChange>=arr[i]){
numCoins = (centsChange - remainder)/arr[i]+ processChange(remainder/100);
return numCoins;
}
}
}
int main(){
float change;
do
{
change = get_float("Enter the changed owed\n");
}while (change<0);
printf("Minimum number of coins returned is %d\n", processChange(change));
}
The code in the for loop in processChange does this:
If remainder is zero, do a calculation and return.
If centsChange is one, return.
If centsChange is at least arr[i], do a calculation and return.
Otherwise, reach the end of the for loop and continue iterating.
As far as the compiler can tell, the value of i will reach four, and control will leave the for loop. At that point, control would flow to the end of the function, where there is no return statement. Thus, the compiler is warning you that control would reach the end of a non-void function. (A “non-void function” is one with a return type that is not void. The return type of processChange is int.)
One way to fix this is to insert a return statement at the end of the function.
Another is to disable compiler warnings for this situation, which you can do with GCC and Clang using the -Wno-return-type command-line switch.
We can see that control cannot actually leave the for statement because, when i is three, arr[i] is one, so centsChange % arr[i] necessarily produces zero, which is assigned to remainder, causing code to flow into the first case above. With GCC and Clang, you can inform the compiler of this by inserting __builtin_unreachable(); as the last statement in the function. That tells the compiler that that point in the code logically cannot be reached by any combination of circumstances within the program. (Using this compiler feature when it is not true that control cannot reach the location will break your program.)
Note that the fact that control cannot leave the for loop for the above reason implies the centsChange == 1 base case is unnecessary. The fact that remainder == 0 must be satisfied at some point means it serves as a base case.
Although this analysis discusses the code as it is, experienced programmers would restructure the code so that none of the above solutions are necessary. There are times when various complications motivate us to use code where the compiler cannot deduce that a certain point is never reached in execution, but we know it is, and the above workarounds may be used in such cases. However, this is not one of them. This code is fairly simple and can be restructured so that control flow is simpler and more apparent to the compiler.
This question already has answers here:
Error handling in C code
(23 answers)
Closed 4 years ago.
I've inherited a C system which makes use of function return values to check for errors and exceptions; something like this:
#define OK 1
int create_cheese(const char * myType, float myCost, char * myCheese) {
// Do Stuff...
if // everything looks good
return 1;
else
return // error/exception code
}
int main() {
rc = create_cheese("cheddar", 12.99, &cheese);
if (rc != OK) { exit(rc); }
return 1;
}
Is this considered proper C technique?
These functions are not very, well, functional. In other words, they don't use the function return values as meaningful data relevant to program execution; they're used for error and exception handling. Thank you, Keith :^)
Is this considered proper C technique?
I have found no matching return 1 on success parallel in the C language or standard library.
C has at least 6 different ways it communicates errors. Many listed below.
OP's posted technique is a variation returning error/success status.
It suffers from 2 weaknesses
OK is too common an identifier and will likely collided with other code. Far too easy to find OK defined as 0.
Opposite zero-ness. OK as 0 is more common.
OP's error approach is not proper as it is yet another unnecessary variation on existing techniques.
Various C error / exception / the unhappy path handling
Return error/success (not data, but maybe error data)
Success is 0, else return a non-zero errno_t: Many ..._s() functions. (Similar to OP's - yet non-zero success)
Success is 0, else non-zero. remove(), raise()
Return data - a few (maybe only one) values indicate error/special.
NULL is bad, else a pointer. fgets(), setlocale(), bsearch()
NULL is maybe bad, else a pointer. malloc
0 is bad, else various others are good. fread()
-1 is bad, else various others are good. time(), fseek()
EOF - a negative. 0 or more is good. fgetc()
FP_ILOGB0, FP_ILOGBNAN else some int. ilogb()
any negative is bad, else value is informative. printf().
Largest few values have special meaning, else some unsigned. mbrtoc16()
Via state/instance object: Many I/O functions return EOF to indicate end of file or I/O error. Other functions used to determine detail. feof(), ferror().
Global
Error code errno: strtol()
Floating-point environment: fegetexceptflag()
Not-a-number: NaN sits in limbo between error codes and values. log(negative_x), atanh(x_more_than_1)
UB: Simply not handling the error in a specified way: e.g. 1/0
One style I do not see with the standard C library, though is other libraries, is bool foo(..., int *error_code) - passing is a place to store/update the error detail.
Since C doesn't support exceptions, the only way to test for an error after a function call is to examine a designated error holding variable. There are a few ways you can do this:
Return the error / success indication from the function (i.e. errno_t foo(int bar);)
Make the error / success indication an output pointer parameter (i.e. void foo(int bar, errno_t *error_code);)
Use an operating system error indicator (i.e. SetLastError on Windows, or errno).
I'm a fan of method #2, since it's a lot harder to ignore that there's an error parameter with:
errno_t err;
myfunction(100, &err);
Instead of:
errno_t err = myfunction(100); // You could just as easily not assign the return value
This is the correct way, but you can follow the rules of the existing macro-constant EXIT_SUCCESS (= 0) and EXIT_FAILURE (= -1). So I suggest to return a value != 0 when there is an error .
Yes, this is perfectly valid way of handling errors, particularly as C doesn't have exceptions like C++ or Java does.
You check the return value to see if the function was successful or not, and use the parameters to get back information from the function. Plenty of library functions work in this manner, so it's a well known idoim in C.
You can also mix returning meaningful values and error codes. For example, the write library function returns the number of bytes read, or -1 if an error occurred. In the case of an error, it also sets the global errno which contains the actual error code.
The convention in C (it's almost formalised in the standard) is to return 0 for success. So your returning 1 is idiosyncratic and ought to be changed.
This can be extended to return negative for failures and positive for near-failures.
When returning from main you ought to assume that the operating system will only deal with 8 bits of the return value, and possibly unsigned too which, maddeningly, is in contradiction with returning a negative for failure! That's the good thing about standards: there are so many to choose from.
So i dont get this error in other programs but i did get it in this.
This program is an example where i dont get the error.
#include<stdio.h>
int main() {
system("pause");
} // end main
but in this program below i get the error
#include <stdio.h>
//#include <stdlib.h>
// Takes the number from function1, calculates the result and returns recursively.
int topla (int n) {
if(n == 1)
return 3;
else
return topla(n-1) + topla(n-1) + topla(n-1);
}
// Takes a number from main and calls function topla to find out what is 3 to the
// power of n
int function1(int n) {
return topla(n);
}
int main() {
int n; // We us this to calculate 3 to the power of n
printf("Enter a number n to find what 3 to the power n is: ");
scanf("%d", &n);
function1(n);
system("pause");
} // end main
Just include stdlib.h, but don't use system("pause") as it's not standard and will not work on every system, just a simple getchar() (or a loop involving getchar() since you've used scanf()) should do the trick.
And normally system("pause") is found in windows command line programs because windows command prompt closes when the program exits, so maybe running the program from the command prompt directly would help, or using an IDE that fixes this like geany.
Finally always check the return value if scanf() instead of assuming that it worked.
Note: This code
return topla(n - 1) + topla(n - 1) + topla(n - 1)
you can write as
return 3 * topla(n - 1);
instead of calling topla() recursively 3 times.
And you don't really need the else because the function returns unless the n != 1 so even without the else the recursion will stop when n == 1.
The system function is declared in the standard header <stdlib.h>. If your program calls system(), you must have
#include <stdlib.h>
at or near the top of your source file.
But part of your question is: why didn't the compiler complain when you omitted the #include directive?
The 1990 C standard (sometimes called "ANSI C") permits calls to functions that have not been explicitly declared. If you write, for example:
system("pause");
with no visible declaration for the system function, it would be assumed that system is declared with a return type of int and parameters matching the arguments in the call -- in this case, a single argument of type char*. That happens to be consistent with the actual declaration of system, so with a C90 compiler, you can get away with omitting the #include directive. And some C compilers that support the more current 1999 and 2011 standards (which don't permit implicit declarations) still permit the old form, perhaps with a warning, for the sake of backward compatibility.
Even given a C90 compiler, there is no advantage to depending on the now obsolete "implicit int" rule. Just add the #include <stdlib.h>. More generally, for any library function you call, read its documentation and #include the header that declares it.
As for why you got an error with one of your programs and not another, I don't have an explanation for that. Perhaps you invoked your compiler with different settings. In any case, it doesn't really matter -- though you might look into how to configure your compiler so it always warns about things like this, so you can avoid this kind of error.
Here you need to know about two things.
Firstly, your code works absolutely fine and the program really finds the value of 3^n. So do not worry about that.
Coming to the system() part,
In order to use the system(); function, you need to include the stdlib.h header file, as the function is declared in that header.
So it is a good practice to include the header (rather than commenting it).
Now, the pause keyword is used in windows, to stop the console from closing after the completion of the program and it is only for windows.
Note that, system("pause"); is also not a standard, and it does not work on other machines, namely linux as, with the system command, you are directly interacting with the command line. In this regard, the commands for each operating system are specific, and they cannot be used for other OS.
so it is better that you use getchar(); , a C standard library function, to hold the console window.