To assert or not to assert...

I

Ian Collins

That's a reasonable objection, but it's one that can easily be fixed up:

assert(is_misbehaving(library) == 0);
assert(is_misbehaving(hardware) == 0);

Define is_misbehaving()!
 
I

ImpalerCore

Ok, let me phrase an assert question on this simple implementation of
strlen.
\code
size_t strlen( const char* s )
{
  size_t len = 0;
  while (s[len] != 0)
    len++;
  return len;
}
\endcode
I can write the implementation to do one of the three things.
1.  Let it bomb as is.
2.  Put in 'assert( s != NULL );'.  When user passes a NULL into it, a
diagnostic message is printed and aborted.
3.  Check s for NULL, and avoid doing anything that would cause the
function to bomb.
\code
size_t strlen( const char* s )
{
  size_t len = 0;
  if ( s )
  {
    while (s[len] != 0)
      len++;
  }
  return len;
}
\endcode
According to Ian's rule if I understand it correctly, the first and
only the first implementation should be used, since it's the arguments
are driven specifically by user input.

Who says the argument to strlen() is driven by user input?  I see
no user input in the code you posted.

Users don't call strlen(); code does.  It's your responsibility
as a programmer to avoid calling strlen() with anything that will
invoke undefined behavior.  If you make such a call in response to
use input, it's because you weren't sufficiently careful in your
handling and checking of the user input.

The strlen() function, as the standard defines it, creates an
implicit contract: it will return the length of the string pointed to
by s as long as s actually points to a string.  It's the caller's
responsibility to meet that requirement.  The user (the person
running your program) isn't a party to the contract.

(Sorry if the above sounds a bit harsh; it wasn't meant that way.)

I think I'm using bad terminology since "user" in the way I'm meaning
it is someone who uses my library. Maybe application developer is a
better term.

| me |--->| library |--->| application developer |--->| application
|--->| user |

If I understand you correctly, it sounds like as a library developer,
I'm allowed to place the responsibility of using the library properly
squarely in the hands of the application developer. For example, if
someone passes a NULL comparison function pointer to my array sort
function, I'm within my rights to crash the application.

So as a library developer, I can use assert in places that are always
supposed to be true given valid user input, rather than using assert
within the library to validate that the user passed the correct
arguments. Is this is right idea?

(and I didn't take it harshly ;)

Best regards,
John D.
 
E

Ersek, Laszlo

Many implementations have a built-in automatic assert for null pointer
access anyway; it is called a crash.

And relying on it is called a "security vulnerability" [0]. Also,

$ ( trap '' SIGSEGV && ./yourcode )

may cause yourcode, relying on *(char *)0 to result in a synchronous
SIGSEGV, to wreak havoc on various resources. (Especially if yourcode is a
setuid root program, although a program designed to be setuid root would
never rely on such behavior in the first place.)

{
char unsigned buf[32767],
*workspace;
ssize_t ss;
size_t used;

workspace = malloc(huuuge); /* returns (void *)0 */

/* proper read() loop with error checking: */
ss = read(fd1, buf, sizeof buf);
/* ... */

(void)memcpy(workspace, buf, used); /* SIGSEGVs ignored, UB hereafter */
disintegrate(buf, sizeof buf);
rot13_or_whatever(workspace, used); /* SIGSEGVs ignored */
(void)memcpy(buf, workspace, used); /* SIGSEGVs ignored */

/* proper write() loop with error checking: */
ss = write(fd2, buf, used);
/* ... */

_exit(0); /* success! */
}

assert is for programming errors. With today's computers, malloc will
never return a null pointer.

This statement is too generic. You may be able and may want to constrain
the memory available to the program. Programs can (and sometimes certainly
do) run out of address space or even backing store (if the system is
conservative(-ly configured)). Just combine the trap above (ie.
bequeathing a SIG_IGN for SIGSEGV to the child process) with "ulimit -v".

So you can use this function instead of malloc:

void* unchecked_malloc (size_t size)
{
void* result = malloc (size);
assert (result != NULL || size == 0);
return result;
}

Please don't. Compiling a correct program with NDEBUG #define-d should not
change its observable behavior, modulo differences in performance. Suppose
a program relying on this technique is compiled by the system distributor
with NDEBUG defined. If the program runs out of memory, then even under
the most benign circumstances, at least the exit status may change from

WIFSIGNALED(status) && SIGABRT == WTERMSIG(status)

to

WIFSIGNALED(status) && SIGSEGV == WTERMSIG(status)

(Let alone introducing the greater problems mentioned above.)

In short, never rely on undefined behavior.

Cheers,
lacos

[0] http://wiki.debian.org/mmap_min_addr
 
E

Ersek, Laszlo

Exactly.

For a great example of how NOT to do this, look at the RPM package
manager, which calls a function which requires root privileges, then
asserts that the call succeeded. Dumb. That's a situation which
requires a diagnostic explaining what went wrong. (I think it first
checks that you have the privileges, but there are other reasons the
call can fail...)

Running rpm on top of pseudo, are we? :)

(Comment made in good faith!)

Cheers,
lacos
 
S

Seebs

Running rpm on top of pseudo, are we? :)

Ayup.

OT digression:
.... and as of a couple of weeks back, that includes emulation of chroot(2),
but that actually reduces the frequency of failures, as we're no longer
relying on a separate library for emulation of chroot(2).

Back on topic:
It's a good example to think about for an assert, though. You make a
call out to the underlying operating system or implementation. Should
you assert() that the operation succeeded? OF COURSE NOT. That would
be ridiculous.

If an operation has a way to indicate that it has failed, it is almost
certainly expected to be POSSIBLE for it to fail. Therefore, you should
not use an assert() to check for it, because that's not a safe assertion.

The intent of an assertion should be that you are quite sure that, short
of someone flipping bits randomly in memory, it will never fire. IMHO.

-s
 
P

Phil Carmody

Richard Heathfield said:
I try to follow that rule myself (although I've never seen it put
quite so eloquently)

I sent that post by accident. I couldn't find a good way of wording
it, so intended to just not send anything. Unfortunately my time
is split between pine, mutt, vi, and emacs, and I really have no
idea what keypress to use in order to quit nowadays. My attempt to
quit sent it instead!
, so it's surprising (and perhaps mildly alarming)
just how often my assertions fire during development!

Which more than justifies them being there. Either you need to
fix your assumptions, or you need to fix your implementation.
When developing, if I never get *any* assertion to fire, I feel
worried as I've apparently not tested enough tough corner cases.
To know the strength, you must test to destruction, so at least
one must fail!

Or in yesterday's words - "oprofile_add_sample will always be
called within an IRQ context, surely?". Hmmm, must go to work,
got a patch to upstream...

Phil
 
P

Phil Carmody

Seebs said:
Exactly.

For a great example of how NOT to do this, look at the RPM package manager,

Do you need a PIN number to use the RPM package number on your
personal PC computer? ;-)
which calls a function which requires root privileges, then asserts that
the call succeeded. Dumb. That's a situation which requires a diagnostic
explaining what went wrong. (I think it first checks that you have the
privileges, but there are other reasons the call can fail...)

So they're _releasing_ the code with assert()s enabled?

Bodgers. Remember kids - don't do RPM.

Phil
 
N

Nick Keighley

Plauger's library book used assert()s in his test harness. A not bad
idea I think.

I consider there are two types of error.
Logic Errors
Runtime Errors

logic errors can, in principle (by the Sufficiently Smart Programmer),
be detected purely by examining the program text. With no idea of the
input or the execution environment.

runtime errors can only be detected (as the name implies) at runtime.

Runtime errors are caused by improperly validated user input or other
runtime input. Resource exhastion (run out of memory or file handles)
or failure of hardware, comms or databases.

Assert()s are for logic errors. In an ideal world thourough validation
(testing, walkthrus etc.) mean that shipped code shouldn't have any
logic errors. Some peopel therefor turn off assert() indelivered code.
Other less perfect programmers leave at least some of the asserts in
on the grounds that detecting an error is better than blundering on.
From a library perspective,

look up Design by Contract. Each function has pre-conditions ("the
count must be greater than zero") which muct be met. In return the
function meets a post-condition ("returns the mean of values in the
array" "the control channel will be set to the new value"). The pre-
conditions in particular can often be enforced by assert()s (post-
conditions tend to be more expensive to compute).

I'll often have "user facing" code do heavy validation and internal
functions rely on assert()s (the parameters should all have been
validated already). I use a "how foreign are they" as my rule of thumb
as what is "user" facing. I trust the code from the guy at the next
desk more than I trust the code from the team in the next building.
Everything that comes over a comms like is potentially crap.
I consider that there are two kinds of
programming errors.  Internal ones within the library, where a user
provides valid input and environment, but due to a logic error I do
something that I detect later because I made an assumption when it
actually was not correct.  Then there are the external errors that are
driven by user behavior, whether by giving bad input, or going behind
the API's back and monkeying around, or problems with system
resources.

I don't think I have enough experience to know where to draw that line
yet.  Are 'condition that should never happen' considered programming
errors if I detect that it happens?

maybe.

PRE-CONDITION (sum-of-active-calls() > maximum-number-of-calls)

should never happen. But what should you do? Assert probably as some
fundamental assumption on which the program is based has been violated
(other bits of code might be using this to calculate how many free
calls there are left- this being negative only boggles the mind). Your
code just isn't ready for the wild.

Other places you might be able to run on after discarding the current
transaction. Drop a call, discard a packet, roll back a partial
database transaction. You've faile dto process something but the
datbase is still intact and other users are getting their stuff done.


you need to decide exactly what "array_erase()" means.

That's the issue, if I pass an invalid 'pos' argument, from the
library perspective, it doesn't harm anything, as I can silently
ignore it and go about my merry business.

not keen in general on this. Might the caller think that he *has*
erased something. Silently ignoring user requests generally seems bad.
Deleting non-exitent array elements generally seems not array-like
behaviour.
 The question is if passing
an invalid 'pos' and silently ignoring it harms the user experience.
My gut is telling me that it is, because the user is likely making a
logical error (overrunning the bounds of the array).  If I assert
'(pos < size)' from the library, it's a message to the library user
that you're violating the contract to gc_array_erase, you *better* do
something about it.

yes, Take a look at exceptions. Either C++ where I enountered 'em or a
slightly less rough ride, learn python.
 I can accomplish the same thing using return
codes or a 'errno = EINVAL' mechanism as well,

errno is pretty generally agreed to be broken
which is telling the
user that *maybe* you should do something about it, and then place the
burden on the user; I just can't quite make the case convincing enough
that using an assert in this scenario is 'The Wrong Thing To Do'.

it depends on the semantics of your array. I'd be pretty brutal (ie
assert) in such a low level class. I'd expect higher level
abstractions to be gentler on the caller.

gc_array_erase- asserts on array bound errors
remove-basestation- returns ITEM_NOT_FOUND if there is no such
basestation

I make no claim to internal consistency

your call. Since the function is called "...insert_sorted" I don't
think it would be unreasonable to expect the array to be sorted.
Perhaps provide a user callable predicate
is_array_sorted (const c_array_t*) that returns TRUE if the array is
sorted. Then the user can avoid making dumb mistakes.
I agree completely.  I'm just trying to grasp what the last resort's
are.  Thanks for your comments.

well I've tried to help, so I'm sure you're thoughly confused!
 
I

ImpalerCore

Plauger's library book used assert()s in his test harness. A not bad
idea I think.

I agree. I'm not at the point where I have a test harness yet, but I
do want to build one. And asserts would be part of the test harness.
I consider there are two types of error.
   Logic Errors
   Runtime Errors

logic errors can, in principle (by the Sufficiently Smart Programmer),
be detected purely by examining the program text. With no idea of the
input or the execution environment.

Would you consider passing NULL to strlen a logic error for the
application developer?
runtime errors can only be detected (as the name implies) at runtime.

Runtime errors are caused by improperly validated user input or other
runtime input. Resource exhastion (run out of memory or file handles)
or failure of hardware, comms or databases.

Assert()s are for logic errors. In an ideal world thourough validation
(testing, walkthrus etc.) mean that shipped code shouldn't have any
logic errors. Some peopel therefor turn off assert() indelivered code.
Other less perfect programmers leave at least some of the asserts in
on the grounds that detecting an error is better than blundering on.

This makes sense to me.
look up Design by Contract. Each function has pre-conditions ("the
count must be greater than zero") which muct be met. In return the
function meets a post-condition ("returns the mean of values in the
array" "the control channel will be set to the new value"). The pre-
conditions in particular can often be enforced by assert()s (post-
conditions tend to be more expensive to compute).

Sounds like I need to be more formal in my documentation. I have a
description of the parameters and a sample program the demonstrates
it, but I don't have a formal precondition/postcondition
documentation.
I'll often have "user facing" code do heavy validation and internal
functions rely on assert()s (the parameters should all have been
validated already). I use a "how foreign are they" as my rule of thumb
as what is "user" facing. I trust the code from the guy at the next
desk more than I trust the code from the team in the next building.
Everything that comes over a comms like is potentially crap.



maybe.

   PRE-CONDITION (sum-of-active-calls() > maximum-number-of-calls)

should never happen. But what should you do? Assert probably as some
fundamental assumption on which the program is based has been violated
(other bits of code might be using this to calculate how many free
calls there are left- this being negative only boggles the mind). Your
code just isn't ready for the wild.

Other places you might be able to run on after discarding the current
transaction. Drop a call, discard a packet, roll back a partial
database transaction. You've faile dto process something but the
datbase is still intact and other users are getting their stuff done.

<snip>




you need to decide exactly what "array_erase()" means.

\code snippet
c_array_t* gc_array_erase( c_array_t* array, size_t pos, size_t
type_sz )
{
size_t pos_memory;

if ( array )
{
if ( pos < array->size )
{
if ( pos != array->size - 1 )
{
pos_memory = pos * type_sz;
memmove( (c_byte*)array->buffer + pos_memory,
(c_byte*)array->buffer + pos_memory + type_sz,
(array->size - pos - 1) * type_sz );
}

--array->size;
}
}

return array;
}
\endcode
not keen in general on this. Might the caller think that he *has*
erased something. Silently ignoring user requests generally seems bad.
Deleting non-exitent array elements generally seems not array-like
behaviour.

I agree. I do feel uncomfortable at the library level just letting a
'pos >= array->size' silently skip by in the above gc_array_erase. I
could make the return value NULL to signify a bad parameter, or make
an assert to enforce the precondition. The implementation above
silently ignores the issue, which is what I want to change.
yes, Take a look at exceptions. Either C++ where I enountered 'em or a
slightly less rough ride, learn python.

I've used them in C++, but that feature isn't easy to implement in C,
and I would be nervous trying to use it for error handling at the
library level.
errno is pretty generally agreed to be broken

Because of multi-threading? Or too many cooks in the kitchen?

What's your opinion on a separate maintained errno like state
specifically for signaling allocation errors (c_enomem)? I use it for
functions that use malloc internally but uses its return value for
something else. If it returns an error, I can check c_enomem to see
if it was its internal malloc or realloc that failed, or something
else.
it depends on the semantics of your array. I'd be pretty brutal (ie
assert) in such a low level class. I'd expect higher level
abstractions to be gentler on the caller.

  gc_array_erase-  asserts on array bound errors
  remove-basestation- returns ITEM_NOT_FOUND if there is no such
basestation

I think that this is the route I'd like to go.
I make no claim to internal consistency




your call. Since the function is called "...insert_sorted" I don't
think it would be unreasonable to expect the array to be sorted.
Perhaps provide a user callable predicate
is_array_sorted (const c_array_t*) that returns TRUE if the array is
sorted. Then the user can avoid making dumb mistakes.

I do have a function that can take an array and a comparison function
and generate a bool that determines whether its in sorted order. The
question is whether to enforce it as a precondition for insert_sorted
in the library via assert (or maybe something like a heavy_assert for
high cost conditions), or leave it to the user to assert the
sortedness of the container at his convenience (like after inserting a
collection of records from a file). At the moment, I'd prefer to not
introduce a heavy assert into insert_sorted.
well I've tried to help, so I'm sure you're thoughly confused!

I would say that I have been thoroughly confused, but I think I'm
slowly starting to get pieces of understanding. Your comments were
very helpful, thanks.

Best regards,
John D.
 
S

Seebs

Do you need a PIN number to use the RPM package number on your
personal PC computer? ;-)
So they're _releasing_ the code with assert()s enabled?

Ayup. And there is no other error checking for that operation. So
the symptom is something to the effect of "assertion failed: xx == 0".

-s
 
E

Ersek, Laszlo

It's a good example to think about for an assert, though. You make a
call out to the underlying operating system or implementation. Should
you assert() that the operation succeeded? OF COURSE NOT. That would
be ridiculous.

If an operation has a way to indicate that it has failed, it is almost
certainly expected to be POSSIBLE for it to fail. Therefore, you should
not use an assert() to check for it, because that's not a safe
assertion.

The intent of an assertion should be that you are quite sure that, short
of someone flipping bits randomly in memory, it will never fire. IMHO.

The SUS defines error conditions for functions with "shall fail" and "may
fail". I seem to remember the existence of functions (thus no specifics,
of course) where you could be sure that a function (on a portable
implementation) would never fail, or never fail with some error, or always
fail with some error, or if it fails, then with some specific error.

.... For example, see sigprocmask() [0]. (Of course, that's not
file-manipulation related, so it likely isn't relevant for pseudo.) The
only "shall fail" condition occurs iff the programmer makes a mistake
calling sigprocmask(). There is no "may fail" condition. Assuming one
manages to pass one of SIG_BLOCK, SIG_SETMASK and SIG_UNBLOCK for /how/, I
think an assert(0 == retval) wouldn't be unreasonable. Especially because
a failing sigprocmask() has both of these characteristics:

- it can break the program horribly,

- it really cannot be worked around or fixed or ignored.

.... I was also thinking about what happens when one passes a null pointer
in /set/. I was kind of expecting an EFAULT for that case. That was a
wrong expectation though, I concluded, because the system doesn't have to
behave in any specific way if I introduce undefined behavior. So the
assert() mentioned above *is* reasonable.

(Nonetheless, I did grep the SUSv4 for EFAULT, with interesting results.)

Cheers,
lacos

[0] http://www.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html
 
E

Ersek, Laszlo

So they're _releasing_ the code with assert()s enabled?

Bodgers. Remember kids - don't do RPM.

:(

man lbzip2

----v----
EXIT STATUS

[...]

SIGABRT
(via assert()) if a runtime assertion fails (ie. lbzip2
detects a bug in itself). Hopefully whoever compiled your
binary wasn't bold enough to #define NDEBUG.
----^----

lbzip2 is absolutely paranoid wrt. error checking, but paraphrasing what
you've put so eloquently: why not assert() that the impossible never
happens?

My assert()'s are not error checking. They mark the places in my code
where a NASA project team would work a week apiece on formally proving
that the assertion always holds, and then replace it with a comment,
holding the reference to the proof in the docs repo.

(This is not to say that I don't (try to) prove formally the more hairy
stuff myself -- it's just that I'm no NASA project team, so I put the
"proof" in the comment, and keep the assert().)

Cheers,
lacos
 
S

Seebs

... For example, see sigprocmask() [0]. (Of course, that's not
file-manipulation related, so it likely isn't relevant for pseudo.)

Ironically, I spent a few hours messing with it recently.
The
only "shall fail" condition occurs iff the programmer makes a mistake
calling sigprocmask(). There is no "may fail" condition. Assuming one
manages to pass one of SIG_BLOCK, SIG_SETMASK and SIG_UNBLOCK for /how/, I
think an assert(0 == retval) wouldn't be unreasonable. Especially because
a failing sigprocmask() has both of these characteristics:
- it can break the program horribly,
- it really cannot be worked around or fixed or ignored.

Interesting point, but I'd think the more reasonable thing would
be to diagnose the failure and indicate the failure of the function
which would have called it.

The typical usage pattern (I know this function isn't standard C, but the
usage pattern is adequately generic) is:

sigprocmask(SIG_BLOCK, &new_signals, &old_values);

/* do stuff */

sigprocmask(SIG_SETMASK, &old_values, NULL);

Now, if sigprocmask fails, you probably don't want to /* do stuff */.

But you might want to have the *entire operation* return a meaningful
failure, possibly with a diagnostic. For instance, let's say that
we use this idiom when about to save a data file. Something goes wrong,
we don't know what, and sigprocmask fails unexpectedly. We could abort()
at this point... But that seems unfriendly! More friendly would be to
report that the save operation failed, and let the user try to do something
else to avoid losing the data.

So even though I can't recover from the failure, in that I can't complete
some operation, I can do something more graceful than coredumping -- I can
report a meaningful failure and *not try to do that*.

-s
 
I

Ian Collins

I agree. I'm not at the point where I have a test harness yet, but I
do want to build one. And asserts would be part of the test harness.
Assertions in the context of a test harness are generally not
implemented with assert(). If they were, you could only detect one test
failure in a run!

Test assertions indicate failure by other means.
 
I

ImpalerCore

Assertions in the context of a test harness are generally not
implemented with assert().  If they were, you could only detect one test
failure in a run!

Test assertions indicate failure by other means.

Hmmm, you're right. Good point.
 
B

BGB / cr88192

ImpalerCore said:
I stumbled across a couple assert threads from a while ago. I seem to
have a hard time figuring out how to use assert properly, and I go
back and forth over how best to represent errors from a library's
perspective. From what I've gathered, assert is intended to catch
programming errors, but I don't really know what that means in the
context when writing a library.

There are three main errors that I try to handle.

1. Memory allocation faults.

These faults are caused when malloc, realloc, or calloc fails. There
are several methods that this can be communicated to the user. For
functions that allocate structures, the return value represents either
the allocated object if successful or returns NULL if allocation
failed. The caller of the function is responsible for handling the
result.

IMO, assert is not good within a library, except if the error is critical,
and has little chance of being useful in a debugger (typically, assert will
call "abort()", which will generally exit the app and make no attempt to
pass control to a debugger). (unless of course one sets the signal handler
for SIGABRT to something invoking the debugger).


better options:
returning a failure status (this is the "old guard" of C);
callbacks (this can handle more obscure cases);
signal, which is standard, although doesn't really allow user-customized
signals;
exceptions (either OS-specific, or involving a custom-made
exception-handling system);
....


recently, I have used exceptions some in my case, but often in the form of
special handling code built on top of OS-specific mechanisms (such as Win32
SEH, ...).

the problem in my case is it is a little bit of a mess, as there are several
different exception-handling systems built on these OS mechanisms, but they
wont correctly deal with each-others' exceptions.

it is also sad that there is no really "good" way to provide an interface
for exception handling in C. my most recent example partly fakes the
object-based mechanisms from Java and C#, but uses a function pointer for
the "try" block (and may return an exception object if an exception occurs).

this differs some from some of my prior systems which had attempted to fake
the "form" of a try/catch block via API calls, which tended to be a bit of a
mess (like longjmp, but worse), and had used a less "well defined" notion of
an exception...

....
 
N

Nick Keighley

built the test harness *really* early
Assertions in the context of a test harness are generally not
implemented with assert().  If they were, you could only detect one test
failure in a run!

and? I'm thinking of a TDD-like approach. You shouldn't be writing
dozens of tests and running them for the first time. On a typical run
there's only one or two new tests (usually one). The accumulated
previous tests are your regression tests. They shouldn't break. The
new test either passes or fails. If a test fails you (hopefully) get
dumped into the debugger. You fix the bug and re-run

Test assertions indicate failure by other means.

I've used this technique, it does work (or can work)
 
E

Ersek, Laszlo

The typical usage pattern (I know this function isn't standard C, but
the usage pattern is adequately generic) is:

sigprocmask(SIG_BLOCK, &new_signals, &old_values);

/* do stuff */

sigprocmask(SIG_SETMASK, &old_values, NULL);

Now, if sigprocmask fails, you probably don't want to /* do stuff */.

But you might want to have the *entire operation* return a meaningful
failure, possibly with a diagnostic. For instance, let's say that we
use this idiom when about to save a data file. Something goes wrong, we
don't know what, and sigprocmask fails unexpectedly. We could abort()
at this point... But that seems unfriendly! More friendly would be to
report that the save operation failed, and let the user try to do
something else to avoid losing the data.

This assumes there is a user, there is a UI, and (or) there's at least one
thing we don't want to lose. Even then, one might say, if sigprocmask()
can't be trusted to do its job, what can, so why continue at all?

Otherwise, I tend to agree, especially wrt. library code.

So even though I can't recover from the failure, in that I can't
complete some operation, I can do something more graceful than
coredumping -- I can report a meaningful failure and *not try to do
that*.

Sometimes there's nothing else to do. But I agree, being friendly is good.

Cheers,
lacos
 
M

Malcolm McLean

Sometimes there's nothing else to do. But I agree, being friendly is good..
Someone passes a null string to strcpy. As the designer of this
fucntion, you could return a -1 error code indicating that youb are
unable to copy the string. However consider the burden on the caller.
Each call has to be wrapped as if(strcpy(dest, src) == -1)
{ error_handler(); } For a few, heavy-duty functions, this may be
acceptable, but not for s frequently-called or simple fucntion.
Then if he's passed a null string to strcpy, something is wrong with
his program anyway. Because of the nature of computer programs it is
extremely difficult to isolate errors. It's quite likely that an
attempt to save state will result in a corrupted state file beign
written, for instance. So just shutting down the program violently is
often the best thing to do. assert() is nice because, in a sense,
caller can make that decision. Most compilers will give an option to
treat assert fails as fatal, or to carry on regardless. Which ypu
choose depends on whether wrong results are better or worse than no
results. Eg a hospital life support system should crash, this is no
worse than a loss of power. A spaceship life support system should
not, because there is no back-up available, an attempt to continue the
program and recover is better than the certain loss of the crew that
will happen if you shut down.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top