To assert or not to assert...

E

Ersek, Laszlo

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.

I agree; being unfriendly is convenient. (And sometimes it's not even
unfriendly, actually.)

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. [...]

And in case you get a core dump, then what state was left available is
still saved in the best way. (Not for carrying on, but for debugging.)

Cheers,
lacos
 
K

Keith Thompson

Malcolm McLean said:
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.

You mean a null pointer, not a null string, right?

[...]
 
I

ImpalerCore

You mean a null pointer, not a null string, right?

I believe that's what he means. Even so, the prototype itself is

char * strcpy ( char * destination, const char * source );

so a library implementation could check whether 'destination' is NULL
and simply return NULL as its return value, yet when I use it, the
library designer decided it was better to have it crash.

This could be justified by saying that string operations are so low
level that it's worth making the assumption that destination will
never be NULL, but again, why not make assert( destination ) part of
the implementation, and as structures become more and more complex,
when to start checking for NULL. GLib seems inconsistent in this
regard, as some functions consider a null GString or GArray fine, but
crash in others.
[...]

--
Keith Thompson (The_Other_Keith) (e-mail address removed)  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
 
I

Ian Collins

I believe that's what he means. Even so, the prototype itself is

char * strcpy ( char * destination, const char * source );

so a library implementation could check whether 'destination' is NULL
and simply return NULL as its return value, yet when I use it, the
library designer decided it was better to have it crash.

Well it could, given the return value is destination. But who checks
the return value of strcpy?
This could be justified by saying that string operations are so low
level that it's worth making the assumption that destination will
never be NULL, but again, why not make assert( destination ) part of
the implementation, and as structures become more and more complex,
when to start checking for NULL.

It could, but that's just one possible failure mode. If the result of
writing through a null pointer has the same end result as assert(
destination ), why bother with the check? Low level functions like
strcpy tend to be optimised for speed.
 
I

ImpalerCore

Well it could, given the return value is destination.  But who checks
the return value of strcpy?

True, but then why is strcpy defined to return a char* of the
destination? Do people actually use the result of strcpy in an
expression or as the argument to another string function? Why not
just make it void?
It could, but that's just one possible failure mode.

So are you implying that if the 'destination' argument being NULL is
the only failure mode to strcpy, it's worth asserting? And when there
are multiple failure modes, it's not?
 If the result of
writing through a null pointer has the same end result as  assert(
destination ), why bother with the check?

Depends on the availability of core files I guess. I don't have core
files available on Windows, or at least I don't know how to generate
them, and on the embedded project I have, no core files either.
Assert at least gives some context of the kind of logic problem
generated, contrast with injecting printf statements to try to detect
where the crash happens, or loading up the program in a debugger and
trying to replicate the problem. I can use assert in an embedded
project if it's defined to send its message through a serial port;
thus I can still get some notification and context when I have a logic
problem.
 Low level functions like
strcpy tend to be optimised for speed.

Now this is a reason that I can understand why it wants to avoid the
whole checking for NULL issue altogether.

Best regards,
John D.
 
K

Keith Thompson

ImpalerCore said:
True, but then why is strcpy defined to return a char* of the
destination? Do people actually use the result of strcpy in an
expression or as the argument to another string function? Why not
just make it void?

Returning the target address makes things like this possible:

strcat(strcpy(s, "hello"), ", world");

That doesn't mean it's a good idea (overflow checking is difficult when
you combine calls this way), but changing the return type to void would
break existing code, something the standard committee tries very
hard to avoid.

[...]
 
I

Ian Collins

True, but then why is strcpy defined to return a char* of the
destination? Do people actually use the result of strcpy in an
expression or as the argument to another string function? Why not
just make it void?

The number of characters copied would be more use, but as Keith said,
it's too late to change.
So are you implying that if the 'destination' argument being NULL is
the only failure mode to strcpy, it's worth asserting? And when there
are multiple failure modes, it's not?

I'm saying there could be more checking.
Depends on the availability of core files I guess. I don't have core
files available on Windows, or at least I don't know how to generate
them, and on the embedded project I have, no core files either.
Assert at least gives some context of the kind of logic problem
generated, contrast with injecting printf statements to try to detect
where the crash happens, or loading up the program in a debugger and
trying to replicate the problem. I can use assert in an embedded
project if it's defined to send its message through a serial port;
thus I can still get some notification and context when I have a logic
problem.

True, that's why different strategies suit different platforms.
 
R

Richard Bos

ImpalerCore said:
I believe that's what he means. Even so, the prototype itself is

char * strcpy ( char * destination, const char * source );

so a library implementation could check whether 'destination' is NULL
and simply return NULL as its return value, yet when I use it, the
library designer decided it was better to have it crash.

Most probably he didn't. Instead, he decided that the best thing was to
use the default behaviour for your operating system, and the designer of
your operating system decided that when a null pointer is dereferenced,
your installed null pointer hook is called, but when you aren't running
under a debugger there is no such hook installed, so the default handler
is used, and _that_ causes a controlled program crash rather than
allowing you to scribble all over your code. It is even possible, though
I do not know how common, that the OS further defers to the hardware
memory handler for scribbling over unallocated memory, and only causes
the crash when that level returns with an error.
This could be justified by saying that string operations are so low
level that it's worth making the assumption that destination will
never be NULL, but again, why not make assert( destination ) part of
the implementation,

Because it causes an extra load for correctly written programs which
don't need this assert(), and adds no useful information for the vast
majority of users. The time is long, long gone when the average computer
luser could even wrap its brains around the fact that there might be a
real difference between "program crashes with no message", "program
crashes with random scribblings" and "program crashes with a message
which looks like random scribblings to us gnarly dudes in Finance but
hold information that those stupid nerds in the computer department
could understand".

Richard
 
I

ImpalerCore

Most probably he didn't. Instead, he decided that the best thing was to
use the default behaviour for your operating system, and the designer of
your operating system decided that when a null pointer is dereferenced,
your installed null pointer hook is called, but when you aren't running
under a debugger there is no such hook installed, so the default handler
is used, and _that_ causes a controlled program crash rather than
allowing you to scribble all over your code. It is even possible, though
I do not know how common, that the OS further defers to the hardware
memory handler for scribbling over unallocated memory, and only causes
the crash when that level returns with an error.

If I read you correctly, it sounds like nowadays assert is the wrong
level of abstraction for checking against dereferenced NULL pointers,
due to operating system maturity. That sounds reasonable to me. Was
there any time when it was needed to provide this kind of context?
Because it causes an extra load for correctly written programs which
don't need this assert(), and adds no useful information for the vast
majority of users. The time is long, long gone when the average computer
luser

I was under the impression that assert was for developers, not
computer users. My understanding is that assert is NDEBUGed out
before it gets to the computer user. If assert is being used as
communicating errors to users, that's a different can of worms. And
this was a topic covered in the other couple of threads I read on
assert.
could even wrap its brains around the fact that there might be a
real difference between "program crashes with no message", "program
crashes with random scribblings" and "program crashes with a message
which looks like random scribblings to us gnarly dudes in Finance but
hold information that those stupid nerds in the computer department
could understand".

I'm curious, do you or any of the other coders around here have used
'assert' at all in your C code within the last year? Until I stumbled
across some assert thread looking for something else, I never
considered using assert in my code. Unless I know how to use 'assert'
right, I really don't want to use it at all.

Best regards,
John D.
 
M

Malcolm McLean

On May 11, 10:06 am, (e-mail address removed) (Richard Bos) wrote:

I'm curious, do you or any of the other coders around here have used
'assert' at all in your C code within the last year?  Until I stumbled
across some assert thread looking for something else, I never
considered using assert in my code.  Unless I know how to use 'assert'
right, I really don't want to use it at all.
Regularly. Generally not for a null pointer, because dereferencing
them has the same effect as an assert fail, on most platforms. But it
helps to document the acceptable parameters for a function.

Here's a fragment.

int writematmatrix(char *fname, char *name, double *data, int m, int
n, int transpose)
{
/* variables snipped */

assert(m > 0);
assert(n > 0);
assert( (m*n)/n == m );

In this case, the function can't work if a matrix dimension is zero,
which isn't something you'd necessarily realise from the name. I also
check for huge matrices which overflow the precision of an integer.
Strictly the check isn't correct because it's UB, however there's UB
and UB.
 
E

Eric Sosman

[...]
I was under the impression that assert was for developers, not
computer users. My understanding is that assert is NDEBUGed out
before it gets to the computer user. If assert is being used as
communicating errors to users, that's a different can of worms. And
this was a topic covered in the other couple of threads I read on
assert.

One reason that "assert is for developers" is that the output
it produces is of use only to the developers. What sense could an
end user make of "assertion error: mugwump.c(42) x>0"? If he's C-
aware he can guess that an `assert(x>0)' failed, and that it was on
line 42 of a source file named mugwump.c, but since he has no copy
of mugwump.c, doesn't know what's in the vicinity of line 42, and
doesn't even know what `x' signifies, how is he supposed to do
anything productive with the information?
I'm curious, do you or any of the other coders around here have used
'assert' at all in your C code within the last year? Until I stumbled
across some assert thread looking for something else, I never
considered using assert in my code. Unless I know how to use 'assert'
right, I really don't want to use it at all.

Yes, I use assert() now and then. Its value to a developer is
that it can make an error visible sooner rather than later, closer
to the moment at which things went off the rails. I'd rather trip
over a `default: assert(0);' in a `switch' that got hold of an
unexpected type code than to have the `switch' do nothing silently
and let the silent no-op cause trouble further down the line. The
shorter the back-trail from visible effect to unknown cause, the
less likely I am to get lost.
 
B

bart.c

Eric Sosman said:
[...]
I was under the impression that assert was for developers, not
computer users. My understanding is that assert is NDEBUGed out
before it gets to the computer user. If assert is being used as
communicating errors to users, that's a different can of worms. And
this was a topic covered in the other couple of threads I read on
assert.

One reason that "assert is for developers" is that the output
it produces is of use only to the developers. What sense could an
end user make of "assertion error: mugwump.c(42) x>0"? If he's C-
aware he can guess that an `assert(x>0)' failed, and that it was on
line 42 of a source file named mugwump.c, but since he has no copy
of mugwump.c, doesn't know what's in the vicinity of line 42, and
doesn't even know what `x' signifies, how is he supposed to do
anything productive with the information?

He doesn't need to. It can just be part of the report the user sends to the
developer.

It can be of more use than having an application crashing badly, or the user
sending in some hex dump of something or other which is a waste of time.
 
E

Eric Sosman

Eric Sosman said:
[...]
I was under the impression that assert was for developers, not
computer users. My understanding is that assert is NDEBUGed out
before it gets to the computer user. If assert is being used as
communicating errors to users, that's a different can of worms. And
this was a topic covered in the other couple of threads I read on
assert.

One reason that "assert is for developers" is that the output
it produces is of use only to the developers. What sense could an
end user make of "assertion error: mugwump.c(42) x>0"? If he's C-
aware he can guess that an `assert(x>0)' failed, and that it was on
line 42 of a source file named mugwump.c, but since he has no copy
of mugwump.c, doesn't know what's in the vicinity of line 42, and
doesn't even know what `x' signifies, how is he supposed to do
anything productive with the information?

He doesn't need to. It can just be part of the report the user sends to
the developer.

... or in other words, "assert is for developers."
It can be of more use than having an application crashing badly, or the
user sending in some hex dump of something or other which is a waste of
time.

From an end user's point of view, a program that aborts because
of a failed assertion is a lot like a program that aborts for some
other reason: The only visible difference is whether the death rattle
says "assertion failure" or "SIGCHOKE." Either way, the program
terminates abruptly without completing its work; from the user's
perspective, both terminations are crashes.

It may happen that an early assertion failure might prevent the
program from doing something irreparable. But if it's a safety matter,
I don't think it wise to rely on a disable-able assert() macro; I'd
code my own check-sanity-before-erasing-the-EEPROM that nobody could
compile out by accident.

Finally, as to the nature and amount of debugging output: assert()
produces a fairly minimal report, which although it's directed at the
developers may be insufficient for their needs. Dumps of this and that,
traces of the last thousand significant events, all sorts of other data
may be of importance in diagnosing a problem. If the developers need
more than assert() provides, it's up to them to develop their own more
elaborate mechanisms. There's no reason to rely solely on assert() if
more is needed.
 
E

Eric Sosman

I think of an assert stopping a running program,
as meaning that the program needs more work.

Yes, that's the intended use. assert(p) means "If p isn't
true, there's a programming error somewhere that needs to be
found and fixed." Fixed, of course, by a developer.
 
I

Ian Collins

Yes, that's the intended use. assert(p) means "If p isn't
true, there's a programming error somewhere that needs to be
found and fixed." Fixed, of course, by a developer.

But not necessary by the developer of the program that crashed. The
hardware or a library it is using may be the culprit.
 
U

Uno

Rui said:
I notice that reading comprehension isn't exactly your strong suit. After all, if you happen to
read what I've wrote you will notice that said that

"you forced everyone who read your post to needlessly scroll down through
an absurdly long quote"

What did you do? You forced everyone to needlessly scroll down through an absurdly long quote. Who
did you forced? Everyone who read your post.

See? That's not hard, is it?

But keep up with the childish name calling. Good form, Uno.

What you missed by your anal retention was the reference to Plauger in
_The C Standard Library_, where he demonstrates better than any text I
have laid eyes on on what to do with assert.

You forced your lousy editorship onto everybody, by your own estimation.

By mine, I don't imagine that I tell Keith Thompson what to read.
 
B

bart.c

Eric Sosman said:
... or in other words, "assert is for developers."


From an end user's point of view, a program that aborts because
of a failed assertion is a lot like a program that aborts for some
other reason: The only visible difference is whether the death rattle
says "assertion failure" or "SIGCHOKE."

You mean, between "assertion error: mugwump.c(42) x>0" and SIGCHOKE. The
former can help narrow down the error more quickly, and might suggest an
interim workaround for the user.
Either way, the program
terminates abruptly without completing its work; from the user's
perspective, both terminations are crashes.

With my applications users could recover from the occasional crash without
losing hours of work. It depends on the kind of application however.
It may happen that an early assertion failure might prevent the
program from doing something irreparable. But if it's a safety matter,
I don't think it wise to rely on a disable-able assert() macro; I'd
code my own check-sanity-before-erasing-the-EEPROM that nobody could
compile out by accident.

Finally, as to the nature and amount of debugging output: assert()
produces a fairly minimal report, which although it's directed at the
developers may be insufficient for their needs. Dumps of this and that,
traces of the last thousand significant events, all sorts of other data
may be of importance in diagnosing a problem.

Log files are also of use, but they can slow things down. So once an assert
failure happens the user could be requested to turn on logging (or use a
special debug version) in case it happens again then more data can be
reported.

I doubt whether binary dumps of memory contents would be of much use to me.
(Not for user applications anyway.)

Some kinds of bugs only happen after billions of iterations, it's not
practical to print too much stuff out, and a dump would only produce a
snapshot of memory, not how it got like that.

So assertions are very useful and easy to put in. And if they are triggered
due to unexpected user input (a corrupt file for example, or just an
unexpected scenario), then they are converted to proper error-checking.
 
K

Keith Thompson

Uno said:
[...]
You forced your lousy editorship onto everybody, by your own estimation.

By mine, I don't imagine that I tell Keith Thompson what to read.

The point is that quoting an extremely long article in its entirety
and adding only a very few lines of original material is considered
rude.

No, you didn't actually *force* anyone to read anything. But when
I saw your article, I was presented with a choice: I could either
ignore it completely, or I could spend substantial time scrolling
down past text I had already read to find any new material.
Just jumping to the end was an option, but not a realistic one;
there could easily have been some interspersed new material halfway
through.

A newsreader that can hide quoted text could have avoided this,
but not everyone has such a newsreader. (Possibly Gnus can be
configured to do this, but I've never felt the need to figure it out,
because most posters trim more reasonably.)

Multiply this (admittedly minor) inconvenience by the number of
people who read your article.

On the other hand, if you had quoted just a few lines of the previous
article, with any snipping clearly marked, then those of us who had
already read it could concentrate on your new text, and anyone who
had missed it could probably jump to the parent article.
 
M

Malcolm McLean

With my applications users could recover from the occasional crash without
losing hours of work. It depends on the kind of application however.
Arw wrong results better or worse than no results? If developing a
video game, usually it's better to try to continue in the hope that
the user doesn't notice the problem, rather than guarantee spoiling
his enjoyment by shutting down the game. If you are analysing
scientific results, no analysis at all is much better than one that
leads you to the wrong conclusion.
 
U

Uno

pete said:
I think of an assert stopping a running program,
as meaning that the program needs more work.

Well, my context has been cut off three times in this thread. First by
Ian. I didn't mind that so much. Second by a Rui, who thinks I was
advertising a book. Third by Keith, because bandwidth cannot support my
comments and his girth.

Anyways, this is how Plauger begins with assert:

$ ls
XASSERT.C
$ cat XASSERT.C
/* _Assert function */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

void _Assert(char *mesg)
{ /* print assertion message and abort */
fputs(mesg, stderr);
fputs(" -- assertion failed\n", stderr);
abort();
}
$

Equally important is how Plauger designs his library:

$ pwd
/home/dan/source/CLIB
$ ls
ASSERT ERRNO LOCALE SETJMP STDLIB _TEST _UNIXVAX
CTYPE FLOAT MATH SIGNAL STRING TIME
_DUMMY _HEADERS README.TXT STDIO _TC86 _UNIX68K

There's alot I have to work out with this yet, but the role of assert
finds no better explanation with the 3 amigos who cut me off.

Legal stuff after sig.
--
Uno

[Rui, you can stop reading.]

$ cat README.TXT
STANDARD C LIBRARY CODE DISK V. 2.0

This diskette contains all the source code from ``The Standard C Library,''
by P.J. Plauger (Englewood Cliffs, N.J.: Prentice-Hall, 1992). It corrects
a number of errors reported after publication. Hence, the code may differ
from the book in small ways.

Fifteen subdirectories have names that match the standard headers. These
contain all the *.c files presented in the text. (NOTE: Directories limits,
stdarg, and stddef are empty.) The directory _headers contains all the *.h
files presented throughout the text. The directory _test contains all the
t*.c files presented as test programs throughout the text.

The directory _unixvax replicates a number of these files. It also contains
the sample version of raise.c described in the running text for the ULTRIX
(UNIX VAX) operating system. The directory _unix68k contains ``dirty''
versions of these files -- with the conventional UNIX names for system
calls. It also contains the file yvals.h modified for the GCC compiler on
Sun UNIX (680X0) workstations. That version of yvals.h shows how to define
_CPS if you wish to use the UNIX time system call directly. The directory
_tc86 contains the files that must be modified for the Turbo C++ (ANSI C)
compiler. Finaly, the directory _dummy contains simple versions of several
files. Typically, these define functions that can fail all the time.

Appendix A: Interfaces summarizes what you have to do to make use of this
code. Please note that it is NOT prepackaged ready to go for any given
operating environment.

To license the right to share the source on a network or multiuser system,
to distribute copies of the source code, or to distribute unlinked object
modules, contact:

Dinkumware, Ltd.
398 Main Street
Concord MA 01742
(e-mail address removed)
http://www.dinkumware.com
+1-508-369-8489
+1-508-371-9014 FAX

Notwithstanding the legalisms that follow, the author shall gratefully
accept any bug reports, suggested fixes, or suggestions for improvement.
With this code disk, you have already benefited in many ways from such
contributions by others. Please send such information to the above
address, or to:

P.J. Plauger
(e-mail address removed)


RIGHTS AND LIMITATIONS NOTICE

The author (P.J. Plauger) and publisher (Dinkumware, Ltd.) have used their
best efforts in preparing this code. These efforts include the development,
research, and testing of the programs to determine their effectiveness. The
author and publisher shall not be liable in any event for incidental or
consequential damages in connection with, or arising out of, the furnishing,
performance, or use of the software.

If you are an individual person who has purchased a single-user copy of the
machine-readable software (a purchaser), or who has been given access to a
properly licensed copy (a licensee), you are an Authorized User. You, the
Authorized User, must treat your copy of the software ``just like a book,''
except that you may install it on a computer and that you may make a copy of
the software solely for backup or archival purposes. You may not install a
copy of the software for use by any other persons, on either a multi-user
computer, a multi-user network, or a single-user computer. If you as a
purchaser transfer (sell or give) your copy of this software to another
person, you must transfer or destroy any other copies you have made, and
you must transfer a printed copy of this Rights and Limitations Notice. A
licensee may not transfer the software.

An Authorized User may also use and distribute this code only as part of
executable images that cannot be linked to other executable images, and only
provided that the following notice is included prominently in the associated
documentation and as part of the executable image:

Portions of this work are derived from The Standard C Library, copyright (c)
1992 by P.J. Plauger, published by Prentice-Hall, and are used with
permission.

Licenses to install copies of the software on multi-user computers or on
multi-user networks, or to distribute binary object modules, or to
distribute copies of the source files (modified or unmodified) are
available from Dinkumware, Ltd. (phone +1-508-369-8489 or send email to
(e-mail address removed)).

NEITHER DINKUMWARE, LTD. NOR P.J. PLAUGER MAKE, AND AUTHORIZED USERS HEREBY
EXPRESSSLY WAIVE, ALL WARRANTIES, EXPRESS OR IMPLIED. ALL WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE OR OF NON INFRINGEMENT
ARE EXPRESSLY EXCLUDED. If Dinkumware, Ltd. is unable to remedy any defect
in The Standard C Library required under the foregoing warranty, the sole
liability of Dinkumware, Ltd. and/or P.J. Plauger's to you shall be limited
to damages actually sustained by you as a result of the unremedied defect
and shall in no event exceed the amount of license fees received hereunder
by Dinkumware, Ltd. as of the date the applicable warranty claim is first
made.

You shall provice no warranties or representations, express or implied, that
give any other user or customer any rights or remedies against Dinkumware,
Ltd. or P.J. Plauger.
$
 

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

Forum statistics

Threads
474,093
Messages
2,570,610
Members
47,230
Latest member
RenaldoDut

Latest Threads

Top