Reputation: 49
I have the following function and I am using assert
in a couple of places. I want to know where I am using assert in a wrong way and why. The first one is wrong because we can't use assert on an user input.
The second one: can we use assert to check if malloc
succeeded?
The rest I still can't figure out why. Can you give any help?
I would like a short explanation of why assert
is good or bad in the given places.
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>
#define num 10
int hh(char *);
Upvotes: 0
Views: 146
Reputation: 26703
I want this answer to provide an alternative view on your assertions, by trying hard to see a meaningful use of the assertion examples. I otherwise agree with the answer(s) which condemns most of them.
Assertions are used to verify assumptions, i.e. to assert that they are true.
Think of an assertion as
Looking at the assertion examples individually:
assert(false)
more often than I thought possible. That makes at least the sense of "I blindly assume that runtime execution will never ever end up here." but is otherwise very strange and requires an extensive (and usually missing) comment explanation.Upvotes: 2
Reputation: 29266
You define what is right and wrong, so it is up to you, but as a general rule you don't want your program logic in asserts (so checking the length of an input string is a poor use).
Just remember: assert is only active in debug mode, and so if you rely on it for error checking (like almost all of your examples), you will have weird things happening in release mode.
i.e. assert is normally a define like:
/* assert() only does something if NDEBUG is NOT defined */
#ifdef NDEBUG
#else
#define assert(x) _assert(x)
#endif
See Where does the -DNDEBUG normally come from? for more info on NDEBUG
You definitely don't want to change the flow using asserts e.g.
assert(some_function()); /* some_function only called if NDEBUG is not defined */
Using assert
to check the return of malloc
: If NDEBUG is not defined the check is not done, so in theory you can go off and access a NULL pointer. I'd say it is NOT safe. See Handling error checking with assert more discussion.
Lets look at your asserts one by one through the simple "only in debug" filter:
assert(argc==2);
/*
* If NDEBUG is defined and argc == 1 you get through,
* so anything that uses argc[1] will give undefined behavior.
* = BAD USE
*/
char *s = malloc(N);
/* 2 */
assert(s!=NULL);
scanf("%s", s
/*
* If NDEBUG is defined and malloc fails you keep going and read into
* NULL = undefined behaviour = BAD
*/
assert(strlen(s)<N);
/*
* If NDEBUG is defined keeps going even in strlen >= N.
* How bad that is depends on the code - if you are checking the size
* before putting it in a buffer, then it's bad.
* To me it is logic in the assert and so I say BAD
*/
/* 4 */
assert(!*(s+strlen(s)));
/*
* First of that's just silly code.
* If that comes to my code review you'll be removing or rewriting
* it - strlen relies on the NUL, so how can it fail?
* It is logic in the assert - If NDEBUG is set you'll keep going
* even if the condition is not met. How bad that is?
*/
/* 5 */
assert(atol(s));
/*
* Whoa. If NDEBUG is set and s = "Hello" we still keep going...
* What is atol("Hello") ?
*/
printf("%ld\n", 100000000/atol(s));
free(s);
return 0;
}
int f(char *s)
{
/* 6 */
assert(s!=NULL);
/*
* Again if NDEBUG is defined the check disappears and so if s = NULL
* then you dereference a NULL pointer which is undefined behavior
*/
return !*s;
Upvotes: 3