When dealing with an integer do I use gets?
What should I use instead of scanf??
scanf is not unreliable. It is just tricky to use for
these reasons:
- it has treatment of whitespace that is awkward for interactive input. It does
not distinguish newlines from other whitespace, so that when, for
instance, it skips leading whitespace, it will swallow an unlimited
number of blank lines typed by the user, making it appear as if the
program is not responding.
- it is hard to know where the stream position indicator is in order to recover
from errors in interactive input.
Other than that, fscanf() is wonderful for scanning things from formatted
data streams such as text files, and sscanf() is very handy for parsing
stuff out of strings! Just not for direct interactive input.
To make an interactive program using standard I/O, the best thing is to read
whole lines from the user and parse them. If there is something wrong, you can
discard the whole line and ask for another one. This provides a simple and
reliable error recovery mechanism that does not confuse the end user.
>When dealing with an integer do I use gets?
You should never use gets, but rather fgets.
>What should I use instead of scanf??
Use fgets() to get a line of input from the user, and then extract things from
it using scanf, or ad-hoc techniques. For jobs that require a more
sophisticated lexical analyzer, write a more sophisticated lexical analyzer!
>I'm a newbie at C sorry,
You don't have to be sorry for this, we all started as newbies :-)
>I've heard alot of people saying scanf is unreliable
scanf is very reliable (according to its specs), but it is very
difficult to recover after a scanf conversion error, i.e. if you use a %d
conversion specifier, but the user types "abc" at run time, you have a
BIG problem.
>but all the C books tell me to use it.
If I were to write a tutorial C book, I'd recommend it, too (until the
last chapter(s)). The beginner has more important things to care about
than bullet-proof user input.
>When dealing with an integer do I use gets?
You NEVER use gets, no matter what the book says. If you want to read
a whole line of input, you use fgets.
>What should I use instead of scanf??
Until you "graduate" from the newbie level, use scanf and be very careful
when you type the input expected by scanf!
When you get familiar with the language and its usage, you forget that
scanf exists and read your input one full line at a time, then parse the
line with sscanf, strto* or whatever method is best suited to your
particular job.
Dan
--
Dan Pop
CERN, IT Division
Email: Dan...@cern.ch
Mail: CERN - IT, Bat. 31 1-014, CH-1211 Geneve 23, Switzerland
> On Mon, 30 Oct 2000 20:43:35 -0500, DogMan <blac...@hotmail.com> wrote:
> >I'm a newbie at C sorry,
> >I've heard alot of people saying scanf is unreliable
> >but all the C books tell me to use it.
>
> scanf is not unreliable. It is just tricky to use for
> these reasons:
>
> - it has treatment of whitespace that is awkward for interactive input. It does
> not distinguish newlines from other whitespace, so that when, for
> instance, it skips leading whitespace, it will swallow an unlimited
> number of blank lines typed by the user, making it appear as if the
> program is not responding.
>
> - it is hard to know where the stream position indicator is in order to recover
> from errors in interactive input.
>
> Other than that, fscanf() is wonderful for scanning things from formatted
> data streams such as text files, and sscanf() is very handy for parsing
> stuff out of strings! Just not for direct interactive input.
Except for the undefined behavior if the value of a numerical
conversion does not fit in the destination type, specifically
mentioned in the standard.
No function, standard library or otherwise, that invokes undefined
behavior when presented with incorrect data is "wonderful" IMNSHO.
YMMV.
> To make an interactive program using standard I/O, the best thing is to read
> whole lines from the user and parse them. If there is something wrong, you can
> discard the whole line and ask for another one. This provides a simple and
> reliable error recovery mechanism that does not confuse the end user.
>
> >When dealing with an integer do I use gets?
>
> You should never use gets, but rather fgets.
>
> >What should I use instead of scanf??
>
> Use fgets() to get a line of input from the user, and then extract things from
> it using scanf, or ad-hoc techniques. For jobs that require a more
> sophisticated lexical analyzer, write a more sophisticated lexical analyzer!
Jack Klein
--
Home: http://jackklein.home.att.net
> In <8tl7tq$5e2$1...@bob.news.rcn.net> "DogMan" <blac...@hotmail.com> writes:
>
> >I'm a newbie at C sorry,
>
> You don't have to be sorry for this, we all started as newbies :-)
>
> >I've heard alot of people saying scanf is unreliable
>
> scanf is very reliable (according to its specs), but it is very
> difficult to recover after a scanf conversion error, i.e. if you use a %d
> conversion specifier, but the user types "abc" at run time, you have a
> BIG problem.
Please quote these specs that claim scanf() is reliable. The spec I
use that specifically states that it is not is the ISO standard:
7.19.6.2 The fscanf function
Synopsis
[#1]
#include <stdio.h>
int fscanf(FILE * restrict stream,
const char * restrict format, ...);
Description
[snip]
[#10] Except in the case of a % specifier, the input item
(or, in the case of a %n directive, the count of input
characters) is converted to a type appropriate to the
conversion specifier. If the input item is not a matching
sequence, the execution of the directive fails: this
condition is a matching failure. Unless assignment
suppression was indicated by a *, the result of the
conversion is placed in the object pointed to by the first
argument following the format argument that has not already
received a conversion result. If this object does not have
an appropriate type, or if the result of the conversion
cannot be represented in the object, the behavior is
undefined.
The quotation above is from C99, but the wording about undefined
behavior is unchanged from ANSI 89/ISO 90. And the sections for
scanf() and sscanf() merely refer back to this, since they come later
in alphabetical order.
> >but all the C books tell me to use it.
>
> If I were to write a tutorial C book, I'd recommend it, too (until the
> last chapter(s)). The beginner has more important things to care about
> than bullet-proof user input.
>
> >When dealing with an integer do I use gets?
>
> You NEVER use gets, no matter what the book says. If you want to read
> a whole line of input, you use fgets.
>
> >What should I use instead of scanf??
>
> Until you "graduate" from the newbie level, use scanf and be very careful
> when you type the input expected by scanf!
>
> When you get familiar with the language and its usage, you forget that
> scanf exists and read your input one full line at a time, then parse the
> line with sscanf, strto* or whatever method is best suited to your
> particular job.
>
> Dan
Friends do not let friends use *scanf() for numeric conversions.
For some examples see:
http://home.att.net/~jackklein/ctips01.html#safe_gets
http://home.att.net/~jackklein/c/code/strtol.html
http://home.att.net/~jackklein/c/code/getint.html
The first one is not especially elegant, nor is it suited to all
programming situations, but all three of these functions are
absolutely safe and will not invoke undefined behavior no matter what
input they are fed.
Suppose that buffer is not initialized...
Suppose that count is not initialized...
char *getsafe(char *buffer, int count);
Suppose that s is not initialized...
Suppose that end_ptr is not initialized...
Suppose that base is not initialized...
long strtol(const char *s, char **end_ptr, int base);
Suppose that s is not initialized...
int getint(const char *s)
There is no such thing as a C function that will not invoke undefined behavior
regardless of input.
The Microsoft library has some functions that can be helpful in that regard.
The function int _CrtIsValidPointer( const void *address, unsigned int size,
int access ); can tell you that sort of thing, but of course, you cannot do
that portably in the C language. This is completely non-portable, of course,
and does not address the uninitialized integers. Hence, even the non-portable
solutions are incomplete.
--
C-FAQ: http://www.eskimo.com/~scs/C-faq/top.html
"The C-FAQ Book" ISBN 0-201-84519-9
C.A.P. FAQ: ftp://cap.connx.com/pub/Chess%20Analysis%20Project%20FAQ.htm
>On 31 Oct 2000 02:51:14 GMT, Dan...@cern.ch (Dan Pop) wrote in
>comp.lang.c:
>
>> In <8tl7tq$5e2$1...@bob.news.rcn.net> "DogMan" <blac...@hotmail.com> writes:
>>
>> >I'm a newbie at C sorry,
>>
>> You don't have to be sorry for this, we all started as newbies :-)
>>
>> >I've heard alot of people saying scanf is unreliable
>>
>> scanf is very reliable (according to its specs), but it is very
>> difficult to recover after a scanf conversion error, i.e. if you use a %d
>> conversion specifier, but the user types "abc" at run time, you have a
>> BIG problem.
>
>Please quote these specs that claim scanf() is reliable. The spec I
>use that specifically states that it is not is the ISO standard:
Don't be silly! All the arithmetic operators invoke undefined behaviour
when signed or FP arithmetic overflow occurs. Do you call them unreliable?
What do you use instead?
>Friends do not let friends use *scanf() for numeric conversions.
What did YOU use, back when you were a beginner still struggling with the
C syntax and concepts?
> In <hEr+OX4xh8p8VD...@4ax.com> Jack Klein <jack...@spamcop.net> writes:
>
> >On 31 Oct 2000 02:51:14 GMT, Dan...@cern.ch (Dan Pop) wrote in
> >comp.lang.c:
> >
> >> In <8tl7tq$5e2$1...@bob.news.rcn.net> "DogMan" <blac...@hotmail.com> writes:
> >>
> >> >I'm a newbie at C sorry,
> >>
> >> You don't have to be sorry for this, we all started as newbies :-)
> >>
> >> >I've heard alot of people saying scanf is unreliable
> >>
> >> scanf is very reliable (according to its specs), but it is very
> >> difficult to recover after a scanf conversion error, i.e. if you use a %d
> >> conversion specifier, but the user types "abc" at run time, you have a
> >> BIG problem.
> >
> >Please quote these specs that claim scanf() is reliable. The spec I
> >use that specifically states that it is not is the ISO standard:
>
> Don't be silly! All the arithmetic operators invoke undefined behaviour
> when signed or FP arithmetic overflow occurs. Do you call them unreliable?
> What do you use instead?
strtol(), strtoul(), and strtod(), all perfectly standard and all
perfectly defined for any valid or invalid input.
> >Friends do not let friends use *scanf() for numeric conversions.
>
> What did YOU use, back when you were a beginner still struggling with the
> C syntax and concepts?
>
> Dan
I never used the scanf() family, it was just too hideous to
contemplate. I used ato... until it crashed on my in a supposedly
robust program. After that, until the strto... functions came around,
I either prechecked the text before passing to ato..., or just went
ahead and wrote my own conversion functions, since the pre check was
more than half the work anyway.
Yes, you caught me. My claim that "...all three of these functions
are absolutely safe and will not invoke undefined behavior no matter
what input they are fed" was (unintentionally) too broad.
It is absolutely impossible to prevent undefined behavior in the event
of programming mistakes such as you listed.
So I hereby publicly retract that statement, and attempt to replace it
with what I meant to say:
If these functions are used properly, they are absolutely safe and
will not invoke undefined behavior no matter what _input data_ they
are fed.
Is that clear enough?
[snip]
> So I hereby publicly retract that statement, and attempt to replace it
> with what I meant to say:
>
> If these functions are used properly, they are absolutely safe and
> will not invoke undefined behavior no matter what _input data_ they
> are fed.
>
> Is that clear enough?
And I might be wrong about the new, restated claim. I would certainly
like to hear about it if I am.
>Suppose that buffer is not initialized...
>Suppose that count is not initialized...
Presumably the function will behave correctly and in accordance with
its defined behavior, but with respect to invalid inputs.
Just because the input data is uninitialised, that does not mean it
doesn't have a value - merely that that value is (probably) not known
or correct. The function is not behaving in an undefined way. Whether
the inputs are fully defined is not the same as whether the function
is defined.
>The Microsoft library has some functions that can be helpful in that regard.
>The function int _CrtIsValidPointer( const void *address, unsigned int size,
>int access ); can tell you that sort of thing, but of course, you cannot do
It can tell you whether the pointer is valid, not whether the
pointed-to buffer has been initialised, nor whether it is the pointer
you intended.
Sometimes, excessive error checking merely makes things more complex
and therefore increases the scope for errors.
More scarey - defensive code frequently hides errors during testing
that can still find a way to surface later, when the code is in real
use. In short, it can lead to excess confidence in faulty code.
Deciding what error checks are necessary is not, therefore, a trivial
thing that can be handled with naive statements like 'all of them' -
there are conflicting issues that must be handled inteligently.
The issue of uninitialised variables is NOT a user input issue, and
therefore the need to check for such problems is NOT a universal
truth. Some checks may be worthwhile when writing central libraries
for higher level programmers to use. But ultimately, it is always
possible for some idiot to put seemingly valid garbage in, whatever
you do to try to catch it.
--
Steve Horne
s...@ttsoftware.co.uk
No. The behaviour is undefined in such circumstancse.
>
> Just because the input data is uninitialised, that does not mean it
> doesn't have a value - merely that that value is (probably) not known
> or correct.
It's indeterminate. It may well have a trap representation. Or it may
not. It's undefined.
> The function is not behaving in an undefined way.
The program is behaving in an undefined way. Therefore, the
"definedness" of the function is undecidable.
> Whether
> the inputs are fully defined is not the same as whether the function
> is defined.
It is not possible, speaking purely in ISO C terms, to separate the
"definedness" of the function from the "definedness" of the program. If
you use void main(), for example, toupper() might break, even when given
'a' as an input. The ISO C Standard does not prevent this.
<snip>
>
> Sometimes, excessive error checking merely makes things more complex
> and therefore increases the scope for errors.
The key here is in the word "excessive" - "excessive" means "more than
the right amount". Therefore, your statement has no real meaning.
What constitutes "the right amount" of error checking is a design
decision which clueful programmers will, on the whole, make correctly.
> More scarey - defensive code frequently hides errors during testing
> that can still find a way to surface later, when the code is in real
> use. In short, it can lead to excess confidence in faulty code.
Then you are writing the wrong kind of defensive code.
> Deciding what error checks are necessary is not, therefore, a trivial
> thing that can be handled with naive statements like 'all of them' -
> there are conflicting issues that must be handled inteligently.
[I love it when people misspell that word...]
"All of them" is indeed naive. "All that are appropriate" is a movable
feast. It's a judgement call, as you rightly say. I think this is the
only correct point you make in this article, which is why I made special
mention of it.
> The issue of uninitialised variables is NOT a user input issue, and
> therefore the need to check for such problems is NOT a universal
> truth.
Non sequitur. The need to check for uninitialised variables is very
important; so important that it should not be deferred until run-time.
It should be part of your programming style to be on the lookout for
such errors constantly; it should also be part of the peer review.
Furthermore, one should be using a compiler which warns against such
things where possible.
> Some checks may be worthwhile when writing central libraries
> for higher level programmers to use. But ultimately, it is always
> possible for some idiot to put seemingly valid garbage in, whatever
> you do to try to catch it.
Yes. Here's some seemingly valid garbage. Do you recognise it?
"I checked by running this code...
#include <stdio.h>
void main (void)
{
char *l_Test = "abc";
l_Test [0] = 'x';
printf ("%s\n", l_Test);
}
And got the output 'xbc' as expected."
I'm sure you'll agree with me that whoever wrote such code has a lot to
learn about ISO C programming before he even thinks about taking on Dann
Corbit in a debate on the subject.
--
Richard Heathfield
"Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
K&R Answers: http://users.powernet.co.uk/eton/kandr2/index.html
> For some examples see:
>
> http://home.att.net/~jackklein/ctips01.html#safe_gets
> http://home.att.net/~jackklein/c/code/strtol.html
> http://home.att.net/~jackklein/c/code/getint.html
>
If we use scanf in a like manner,
scanf("%d", &i); while(getchar()!='\n');
What can go wrong? I guess I'm a beginner too when it comes to these
things.
Mihai
> And I might be wrong about the new, restated claim. I would certainly
> like to hear about it if I am.
Well, it's not catastrophic, but if you start out with a buffer of 25
characters, and then the user types in 25 white spaces and then a number,
the routines will not have worked properly.
I think the ideal gets would read one character at a time, and reallocate
the string when its size reaches a power of 2.
char *getsafe(void)
{
int n;
char *c;
c = malloc(1);
n = 0;
for(;;){
if((n ^(n-1)) > n - 1){
c = realloc(c, n * 2);
}
if((c[n] = getchar()) == '\n'){
c[n] = 0;
return c;
}
}
}
There, I haven't tested it, but something like it should do the trick!
Mihai
> There, I haven't tested it, but something like it should do the trick!
I have tested my routine and found an omission, but the new code works
great. It will function properly until the user types enough characters to
run out of virtual memory!
char *getsafe(void)
{
int n;
char *c;
c = malloc(1);
n = 0;
for(;;){
if((n ^(n-1)) > n - 1){
c = realloc(c, n * 2);
}
if((c[n] = getchar()) == '\n'){
c[n] = 0;
return c;
}
n ++;
}
}
Input from user:
WHATDOYOUWANTMETODOIDONTUNDERSTANDCOMPUTERZ
This gives i an undefined value with which you can do nothing useful -
you can't even look at it in a defined way.
Better to capture the input as a string and parse it yourself.
http://www.qwikpages.com/backstreets/cbfalconer/newsbits/cstdio_c.htm
I have done nothing further with it, as the need has not arisen.
--
Chuck Falconer (cbfal...@my-deja.com)
http://www.qwikpages.com/backstreets/cbfalconer/
Sent via Deja.com http://www.deja.com/
Before you buy.
> I wrote:
>
> > There, I haven't tested it, but something like it should do the trick!
>
> I have tested my routine and found an omission, but the new code works
> great. It will function properly until the user types enough characters to
> run out of virtual memory!
Mihai...
Another (similar) workaround that you may find interesting is at
http://www.iedu.com/mrd/c (getsm.c and getsmx.c) which reads one character at
a time recursively, then does a single malloc() and stores the characters into
the malloc()ed buffer as the recursion "unwinds".
Morris Dovey
West Des Moines, Iowa USA
>On 31 Oct 2000 06:12:21 GMT, Dan...@cern.ch (Dan Pop) wrote in
>comp.lang.c:
>
>> In <hEr+OX4xh8p8VD...@4ax.com> Jack Klein <jack...@spamcop.net> writes:
>>
>> >On 31 Oct 2000 02:51:14 GMT, Dan...@cern.ch (Dan Pop) wrote in
>> >comp.lang.c:
>> >
>> >> In <8tl7tq$5e2$1...@bob.news.rcn.net> "DogMan" <blac...@hotmail.com> writes:
>> >>
>> >> >I'm a newbie at C sorry,
>> >>
>> >> You don't have to be sorry for this, we all started as newbies :-)
>> >>
>> >> >I've heard alot of people saying scanf is unreliable
>> >>
>> >> scanf is very reliable (according to its specs), but it is very
>> >> difficult to recover after a scanf conversion error, i.e. if you use a %d
>> >> conversion specifier, but the user types "abc" at run time, you have a
>> >> BIG problem.
>> >
>> >Please quote these specs that claim scanf() is reliable. The spec I
>> >use that specifically states that it is not is the ISO standard:
>>
>> Don't be silly! All the arithmetic operators invoke undefined behaviour
>> when signed or FP arithmetic overflow occurs. Do you call them unreliable?
>> What do you use instead?
>
>strtol(), strtoul(), and strtod(), all perfectly standard and all
>perfectly defined for any valid or invalid input.
I was asking about your replacements for the "unreliable" arithmetic
operators.
It is trivial to use scanf reliably, with no risk of undefined behaviour.
>> >Friends do not let friends use *scanf() for numeric conversions.
>>
>> What did YOU use, back when you were a beginner still struggling with the
>> C syntax and concepts?
>>
>> Dan
>
>I never used the scanf() family, it was just too hideous to
>contemplate. I used ato... until it crashed on my in a supposedly
>robust program.
Robustness is the last concern of someone who's still struggling with the
C syntax and concepts. ato* is the wrong solution for *any* kind of
programmer, from the rookie to the expert.
>Mihai Cartoaje wrote:
>>
>> Jack Klein wrote:
>>
>> > For some examples see:
>> >
>> > http://home.att.net/~jackklein/ctips01.html#safe_gets
>> > http://home.att.net/~jackklein/c/code/strtol.html
>> > http://home.att.net/~jackklein/c/code/getint.html
>> >
>>
>> If we use scanf in a like manner,
>>
>> scanf("%d", &i); while(getchar()!='\n');
>>
>> What can go wrong? I guess I'm a beginner too when it comes to these
>> things.
>
>Input from user:
>
>WHATDOYOUWANTMETODOIDONTUNDERSTANDCOMPUTERZ
>
>This gives i an undefined value with which you can do nothing useful -
>you can't even look at it in a defined way.
Wrong! This leaves i unchanged. And this can be checked, by looking at
the value returned by scanf. The really bad input for his code is:
1234567890123456789012345678901234567890
because this invokes undefined behaviour *before* the function returns,
so there is nothing the programmer can do to prevent it from happening,
using this conversion specifier. Fortunately, the conversion specifier
can be modified so that undefined behaviour can NEVER occur.
>Better to capture the input as a string and parse it yourself.
Once you've "graduated" from the beginner level. If your current "job" is
to try to figure out how to write a program that reads 10 numbers from
the user and then displays the minimum and the maximum, scanf is the
right tool for the job.
I can believe that. I thought it was undefined, but I'm happy to admit
defeat on this occasion.
> This leaves i unchanged. And this can be checked, by looking at
> the value returned by scanf.
Fair enough. I should know better by now than to answer articles
concerning scanf.
> The really bad input for his code is:
>
> 1234567890123456789012345678901234567890
>
> because this invokes undefined behaviour *before* the function returns,
> so there is nothing the programmer can do to prevent it from happening,
> using this conversion specifier.
You added that last clause for a reason, I suspect.
> Fortunately, the conversion specifier
> can be modified so that undefined behaviour can NEVER occur.
Ah, thought so.
I don't think I'm ready for scanf yet. Perhaps when I'm a bit older...
>
> >Better to capture the input as a string and parse it yourself.
>
> Once you've "graduated" from the beginner level. If your current "job" is
> to try to figure out how to write a program that reads 10 numbers from
> the user and then displays the minimum and the maximum, scanf is the
> right tool for the job.
Now here is where we must either agree to disagree or "have words" and
/then/ agree to disagree. :-)
Here are some of the problems with teaching scanf as the "newbie default
input function" (not all of these problems are scanf's fault, by the
way):
1) You can't capture "strings containing spaces" intuitively (by which I
mean "%s"!).
2) You can't capture numbers reliably and intuitively (by which I mean
"%d", "%f", etc).
3) You have to learn the Deep Magic of &, and also know when /not/ to
use it.
4) Dealing with whitespace between data required by successive calls is
non-intuitive.
5) Tutors don't lay sufficient stress on checking return values - this
is a big problem when using scanf, because the return value is very
important.
That'll do for now.
Perhaps when I have growed up proper I will start using scanf. For now,
though, I'll stick to my nice, safe fgets.
>Steve Horne wrote:
>>
>> On Mon, 30 Oct 2000 22:39:25 -0800, "Dann Corbit"
>> <dco...@solutionsiq.com> wrote:
>>
>> >Suppose that buffer is not initialized...
>> >Suppose that count is not initialized...
>>
>> Presumably the function will behave correctly and in accordance with
>> its defined behavior, but with respect to invalid inputs.
>
>No. The behaviour is undefined in such circumstancse.
<snip>
>> Just because the input data is uninitialised, that does not mean it
>> doesn't have a value - merely that that value is (probably) not known
>> or correct.
>
>It's indeterminate. It may well have a trap representation. Or it may
>not. It's undefined.
My initial thought - you cannot put a trap representation in a buffer
without the risk that it is coincedentally the same as some real input
data.
My second thought - that is a practical reality a lot of debuggers
etc, but not an absolute fact. A compiler could be written which adds
code and reserves extra memory for such checks.
OK - you are correct - the function could have undefined failure
behaviour.
However...
>> The function is not behaving in an undefined way.
>The program is behaving in an undefined way. Therefore, the
>"definedness" of the function is undecidable.
The part is not the whole. The function is a component of the program,
not the whole program. This is like claiming your windscreen is broken
just because your engine won't start.
>> Whether
>> the inputs are fully defined is not the same as whether the function
>> is defined.
>
>It is not possible, speaking purely in ISO C terms, to separate the
>"definedness" of the function from the "definedness" of the program. If
>you use void main(), for example, toupper() might break, even when given
>'a' as an input. The ISO C Standard does not prevent this.
Pardon!
OK, strictly I know that 'void main()' is an error, and potentially
some implementations might seem to accept it yet generate a
misbehaving program. But how does that affect 'toupper'?
If you are saying that 'nothing is defined if it can be broken by an
external influence', where that influence here is the 'void main()',
then I think you have an impractical definition of 'defined'. After
all, by your definition, nothing in the universe is defined bar
(maybe) the universe as a whole, and the word 'defined' has no useful
meaning.
>> Sometimes, excessive error checking merely makes things more complex
>> and therefore increases the scope for errors.
>
>The key here is in the word "excessive" - "excessive" means "more than
>the right amount". Therefore, your statement has no real meaning.
Taken out of context, yes. However, in the original context where Dann
Corbit seems to be advocating run-time checks of every possible error
condition, I think it is perfectly clear what I mean.
>What constitutes "the right amount" of error checking is a design
>decision which clueful programmers will, on the whole, make correctly.
>
>> More scarey - defensive code frequently hides errors during testing
>> that can still find a way to surface later, when the code is in real
>> use. In short, it can lead to excess confidence in faulty code.
>
>Then you are writing the wrong kind of defensive code.
No - I am arguing that other people should stop writing the wrong kind
of defensive code.
>> Deciding what error checks are necessary is not, therefore, a trivial
>> thing that can be handled with naive statements like 'all of them' -
>> there are conflicting issues that must be handled inteligently.
>
>[I love it when people misspell that word...]
Why? - spelling ability and intelligence are not the same thing. In
fact, perfect spelling at all times could be seen as a sign of
obsessive-compulsive disorder.
No - not an accusation - just something for ultra-pedants to chew on.
>"All of them" is indeed naive. "All that are appropriate" is a movable
>feast. It's a judgement call, as you rightly say. I think this is the
>only correct point you make in this article, which is why I made special
>mention of it.
My god - I got something right. The fabric of the universe is about to
tear!!! ;-)
>> The issue of uninitialised variables is NOT a user input issue, and
>> therefore the need to check for such problems is NOT a universal
>> truth.
>
>Non sequitur. The need to check for uninitialised variables is very
>important; so important that it should not be deferred until run-time.
I was obviously referring to run-time checks.
>It should be part of your programming style to be on the lookout for
>such errors constantly; it should also be part of the peer review.
>Furthermore, one should be using a compiler which warns against such
>things where possible.
I agree absolutely. Except that this leads logically to the conclusion
that you should never use the C language, as it does not allow
complete static checking. At the risk of being flamed to death, this
logic strongly suggests we should all be programming in ada.
But - oh no! - ada has flaws too! Its syntax is wordier, often
obscuring the readability of code that is short and sweet in C, and
therefore making maintenance occasionally more difficult.
Could it be that NOTHING IS PERFECT!!!
>> Some checks may be worthwhile when writing central libraries
>> for higher level programmers to use. But ultimately, it is always
>> possible for some idiot to put seemingly valid garbage in, whatever
>> you do to try to catch it.
>
>Yes. Here's some seemingly valid garbage. Do you recognise it?
<snip the first of several stupid mistakes by me from another thread>
>I'm sure you'll agree with me that whoever wrote such code has a lot to
>learn about ISO C programming before he even thinks about taking on Dann
>Corbit in a debate on the subject.
No - I simply made a human error. Of course if Dann Corbit is
infallible, then I have only one argument in favour of staying in the
debate...
An infallible person cannot make an error, and therefore surely cannot
fully understand the need for and practical application of error
checking.
In the meantime, I shall continue to contribute, hoping to learn more
myself and perhaps even raise something that someone else finds
useful.
--
Steve Horne
s...@ttsoftware.co.uk
>Dan Pop wrote:
>>
>> In <39FEC108...@antlimited.com> Richard Heathfield <ric...@antlimited.com> writes:
>>
>> >Mihai Cartoaje wrote:
>> >>
>> >> If we use scanf in a like manner,
>> >>
>> >> scanf("%d", &i); while(getchar()!='\n');
>> >>
>> >> What can go wrong? I guess I'm a beginner too when it comes to these
>> >> things.
>> >
>> >Input from user:
>> >
>> >WHATDOYOUWANTMETODOIDONTUNDERSTANDCOMPUTERZ
>> >
>> >This gives i an undefined value with which you can do nothing useful -
>> >you can't even look at it in a defined way.
>>
>> Wrong!
>
>I can believe that. I thought it was undefined, but I'm happy to admit
>defeat on this occasion.
If a conversion fails, the function terminates without writing anything
through the corresponding pointer.
>> This leaves i unchanged. And this can be checked, by looking at
>> the value returned by scanf.
>
>Fair enough. I should know better by now than to answer articles
>concerning scanf.
What's wrong with reading scanf's specs? :-)
>> >Better to capture the input as a string and parse it yourself.
>>
>> Once you've "graduated" from the beginner level. If your current "job" is
>> to try to figure out how to write a program that reads 10 numbers from
>> the user and then displays the minimum and the maximum, scanf is the
>> right tool for the job.
>
>Now here is where we must either agree to disagree or "have words" and
>/then/ agree to disagree. :-)
>
>Here are some of the problems with teaching scanf as the "newbie default
>input function" (not all of these problems are scanf's fault, by the
>way):
>
>1) You can't capture "strings containing spaces" intuitively (by which I
>mean "%s"!).
>2) You can't capture numbers reliably and intuitively (by which I mean
>"%d", "%f", etc).
>3) You have to learn the Deep Magic of &, and also know when /not/ to
>use it.
>4) Dealing with whitespace between data required by successive calls is
>non-intuitive.
>5) Tutors don't lay sufficient stress on checking return values - this
>is a big problem when using scanf, because the return value is very
>important.
>
>That'll do for now.
Having learned C from a book using scanf (K&R1) I found all of the above
to be non-issues in the learning process. Once you start writing real
programs, the limitations and gotchas of scanf become evident and you
simply stop using it.
>Perhaps when I have growed up proper I will start using scanf. For now,
>though, I'll stick to my nice, safe fgets.
I've never argued that there is any use for scanf *after* the basics of
the language have been acquired.
>If we use scanf in a like manner,
>
> scanf("%d", &i); while(getchar()!='\n');
>
>What can go wrong? I guess I'm a beginner too when it comes to these
>things.
Imagine you are reading multiple values, such as...
scanf("%d", &int_var ); while(getchar()!='\n');
scanf("%s", string_buf); while(getchar()!='\n');
and consider what would happen on the following inputs...
"5\nfred\n" - Should be ok
"5\n\nfred\n" - Are the two newlines treated as one
whitespace or not? I don't know.
"fred\n5\n" - Oops - user inputs dodgy data
What will the string_buf contain?
could be "fred", could be "5", again
I don't know.
Someone here can probably tell you exactly - I admit I don't remember
- but it really isn't worth the hassle.
--
Steve Horne
s...@ttsoftware.co.uk
<snip>
> >> Presumably the function will behave correctly and in accordance with
> >> its defined behavior, but with respect to invalid inputs.
> >
> >No. The behaviour is undefined in such circumstancse.
I appear to have misspelled (or, rather, mistyped) "circumstances". My
apologies.
>
> <snip>
>
> >> Just because the input data is uninitialised, that does not mean it
> >> doesn't have a value - merely that that value is (probably) not known
> >> or correct.
> >
> >It's indeterminate. It may well have a trap representation. Or it may
> >not. It's undefined.
>
> My initial thought - you cannot put a trap representation in a buffer
> without the risk that it is coincedentally the same as some real input
> data.
The C programmer has no business "putting" a trap representation
anywhere. What exists in a buffer before that buffer is initialised is
indeterminate, so accessing it invokes undefined behaviour.
>
> My second thought - that is a practical reality a lot of debuggers
> etc, but not an absolute fact. A compiler could be written which adds
> code and reserves extra memory for such checks.
Yes, but the ISO C Standard does not mandate it, and specific
implementations do not supersede the definition of ISO C, which is what
we discuss in comp.lang.c.
>
> OK - you are correct - the function could have undefined failure
> behaviour.
>
> However...
>
> >> The function is not behaving in an undefined way.
>
> >The program is behaving in an undefined way. Therefore, the
> >"definedness" of the function is undecidable.
>
> The part is not the whole. The function is a component of the program,
> not the whole program. This is like claiming your windscreen is broken
> just because your engine won't start.
No, it isn't. A C program is not a car. It is a list of instructions to
a C implementation. C implementations are under an obligation to ensure
that a correct program is compiled correctly, but they have real world
objectives too - marketing objectives, such as runtime speed, for
example. This is recognised by the Standard, which basically implies
"you do what you like, as long as it doesn't break correct programs".
This is sometimes called the "as if" rule. Now, what works for correct
programs may well not work for incorrect programs. Carefully crafted
interdependencies within the object code could easily be broken in
unexpected places by a rogue buffer. We do not have the right to assume
otherwise. I have (vicariously) experienced the perils of leaving
insufficient space for the null terminating character on a string -
"Formatting drive C: - proceed (Y/N)?" (hey, this guy was lucky - he was
asked). Undefined behaviour can set off pretty well anything the machine
is physically capable of doing, including random emails to important
people, for example (but no, I never saw that one actually happen...).
> >> Whether
> >> the inputs are fully defined is not the same as whether the function
> >> is defined.
> >
> >It is not possible, speaking purely in ISO C terms, to separate the
> >"definedness" of the function from the "definedness" of the program. If
> >you use void main(), for example, toupper() might break, even when given
> >'a' as an input. The ISO C Standard does not prevent this.
>
> Pardon!
I stand by every word I said.
>
> OK, strictly I know that 'void main()' is an error, and potentially
> some implementations might seem to accept it yet generate a
> misbehaving program. But how does that affect 'toupper'?
I think I've covered this above.
>
> If you are saying that 'nothing is defined if it can be broken by an
> external influence', where that influence here is the 'void main()',
> then I think you have an impractical definition of 'defined'.
No, I have a healthily nervous definition, and one which is in
accordance with the ISO C Standard, which forms the basis of the
programs and techniques we discuss here.
> After
> all, by your definition, nothing in the universe is defined bar
> (maybe) the universe as a whole, and the word 'defined' has no useful
> meaning.
I won't attempt to define "defined". Here's the definition of
"undefined", as far as comp.lang.c is concerned:
+++ quote +++
behavior, upon use of a nonportable or erroneous program construct, of
erroneous data, or of indeterminately valued objects, for which this
International Standard imposes no
requirements
NOTE Possible undefined behavior ranges from ignoring the situation
completely with unpredictable results, to behaving during translation or
program execution in a documented
manner characteristic of the environment (with or without the issuance
of a diagnostic message), to terminating a translation or execution
(with the issuance of a diagnostic
message).
+++ end quote +++
<snip>
> >> More scarey - defensive code frequently hides errors during testing
> >> that can still find a way to surface later, when the code is in real
> >> use. In short, it can lead to excess confidence in faulty code.
> >
> >Then you are writing the wrong kind of defensive code.
>
> No - I am arguing that other people should stop writing the wrong kind
> of defensive code.
I think we might be arguing at cross-purposes. Let me give you an
example of what I consider to be an appropriate level of error-checking,
and you can see whether we do in fact agree. Here's the first part of an
implementation of a function to populate an object stored in some
dynamic data structure or other:
#include <string.h>
#include <assert.h>
#include "foo.h"
int RetrieveFromFoo(void *Object, size_t MaxSize, const FOO *Foo)
{
int Status = FOO_SUCCESS;
assert(Object != NULL); /* program error */
assert(Foo != NULL); /* program error */
assert(Foo->Object != NULL; /* program error */
if(MaxSize < Foo->Size)
{
Status = FOO_OBJECT_TOO_SMALL; /* data error */
}
else
{
memcpy(Object, Foo->Object, MaxSize); /* all okay */
}
return Status;
}
Now, I consider the amount of error processing in this function to be
necessary and sufficient. If rogue pointers come in via Object or Foo,
there's nothing I can do about it in terms of ISO C. I can debug, and
fix elsewhere, but I can't programmatically handle that eventuality
here. So I don't.
Does Your Mileage Vary?
>
> >> Deciding what error checks are necessary is not, therefore, a trivial
> >> thing that can be handled with naive statements like 'all of them' -
> >> there are conflicting issues that must be handled inteligently.
> >
> >[I love it when people misspell that word...]
>
> Why? - spelling ability and intelligence are not the same thing. In
> fact, perfect spelling at all times could be seen as a sign of
> obsessive-compulsive disorder.
Alternatively, it could be seen as an indicator that the person
considers correctness to be an important goal of a programmer. Anyway, I
forgot the smiley, and didn't mean to get on your case about spelling.
There are indeed more important things to discuss in comp.lang.c.
> No - not an accusation - just something for ultra-pedants to chew on.
My own view is that ultra-pedantry is an improvement on pedantry.
> >"All of them" is indeed naive. "All that are appropriate" is a movable
> >feast. It's a judgement call, as you rightly say. I think this is the
> >only correct point you make in this article, which is why I made special
> >mention of it.
>
> My god - I got something right. The fabric of the universe is about to
> tear!!! ;-)
That's unlikely. Unfortunately, the C Standard does not guarantee that
the fabric of the universe will /not/ tear when we get things /wrong/.
That's one reason why it's so important to get things right.
<snip>
> >It should be part of your programming style to be on the lookout for
> >such errors constantly; it should also be part of the peer review.
> >Furthermore, one should be using a compiler which warns against such
> >things where possible.
>
> I agree absolutely. Except that this leads logically to the conclusion
> that you should never use the C language, as it does not allow
> complete static checking. At the risk of being flamed to death, this
> logic strongly suggests we should all be programming in ada.
I think it would be more appropriate to conclude that, when you are
using the C language, you should do your utmost to ensure that the
programs you write work in accordance with the ISO C Standard and
provide an appropriate amount of error-checking.
>
> But - oh no! - ada has flaws too! Its syntax is wordier, often
> obscuring the readability of code that is short and sweet in C, and
> therefore making maintenance occasionally more difficult.
I find that a sensible level of error-checking code does not get in the
way of C's natural flair and elegance.
> Could it be that NOTHING IS PERFECT!!!
No. My wife is perfect. :-)
> <snip the first of several stupid mistakes by me from another thread>
>
> >I'm sure you'll agree with me that whoever wrote such code has a lot to
> >learn about ISO C programming before he even thinks about taking on Dann
> >Corbit in a debate on the subject.
>
> No - I simply made a human error. Of course if Dann Corbit is
> infallible, then I have only one argument in favour of staying in the
> debate...
Dann Corbit is not infallible. I did not claim such a thing. Anyway,
Dann would be the first (well, okay, maybe the third or fourth) to
acknowledge that he is fallible.
I was just pointing out that Dann knows a lot more about C than you or I
do, so it might be wise to tread carefully.
> An infallible person cannot make an error, and therefore surely cannot
> fully understand the need for and practical application of error
> checking.
Since nobody on this planet (except my wife) is infallible, the issue
doesn't arise.
> In the meantime, I shall continue to contribute, hoping to learn more
> myself and perhaps even raise something that someone else finds
> useful.
Good. Just go easy on the fights until you've done some more work on
that left hook, okay? ;-)
Sorry to agree with you, Steve, [ ;-) ] but you have just summed up my
feelings toward scanf() with 100% accuracy.
>Dan Pop wrote:
>>
>> In <39FEC108...@antlimited.com> Richard Heathfield <ric...@antlimited.com> writes:
>>
>> >Mihai Cartoaje wrote:
>> >>
>> >> If we use scanf in a like manner,
>> >>
>> >> scanf("%d", &i); while(getchar()!='\n');
>> >>
>> >> What can go wrong? I guess I'm a beginner too when it comes to these
>> >> things.
>> >
>> >Input from user:
>> >
>> >WHATDOYOUWANTMETODOIDONTUNDERSTANDCOMPUTERZ
>> >
>> >This gives i an undefined value with which you can do nothing useful -
>> >you can't even look at it in a defined way.
>>
>> Wrong!
>
>I can believe that. I thought it was undefined, but I'm happy to admit
>defeat on this occasion.
>
>> This leaves i unchanged. And this can be checked, by looking at
>> the value returned by scanf.
>
>Fair enough. I should know better by now than to answer articles
>concerning scanf.
The usefulness of that check, though, is quite often limited because
whatever value you use to detect 'unchanged' could potentially be a
valid input value.
<snip>
>Ah, thought so.
>
>I don't think I'm ready for scanf yet. Perhaps when I'm a bit older...
Is it worth it?
sscanf occasionally, where you've already validated the input, yes.
Some compilers provide no other way to interpret a float, for example,
short of writing the algorithm yourself. And you can get some odd
results from naive implementations, due to the way rounding errors
accumulate.
BTW - can anyone refer me to a good algorithm for interpreting floats.
I know the naive implementation can have some odd problems, but I'd
like to see how they are
>Here are some of the problems with teaching scanf as the "newbie default
>input function" (not all of these problems are scanf's fault, by the
>way):
I agree on all your points except...
>5) Tutors don't lay sufficient stress on checking return values
I think still hear the echoes of lectures about verification and
validation.
--
Steve Horne
s...@ttsoftware.co.uk
Pass. Do you have any picture questions? ;-)
>
> <snip>
>
> >Ah, thought so.
> >
> >I don't think I'm ready for scanf yet. Perhaps when I'm a bit older...
>
> Is it worth it?
No.
>
> sscanf occasionally, where you've already validated the input, yes.
> Some compilers provide no other way to interpret a float, for example,
> short of writing the algorithm yourself. And you can get some odd
> results from naive implementations, due to the way rounding errors
> accumulate.
Yes. Using floating points is an art in itself. I find it easier to sink
the damn things first.
>
> BTW - can anyone refer me to a good algorithm for interpreting floats.
> I know the naive implementation can have some odd problems, but I'd
> like to see how they are
I've had problems using atof() on mainframes. Perhaps you have
experienced similar problems. I generally find strtod() does the right
thing, though. If that doesn't turn the trick for you, either check
Plauger ("The Standard C Library" - ISBN 0-13-131509-9) or ask Dann
Corbit. :-)
<snip>
>
> I agree on all your points except...
>
> >5) Tutors don't lay sufficient stress on checking return values
>
> I think still hear the echoes of lectures about verification and
> validation.
Well, okay, here's a rewording:
5) Students don't listen to tutors enough with regard to checking return
values.
:-)
>I'm a newbie at C sorry,
>I've heard alot of people saying scanf is unreliable
>but all the C books tell me to use it.
>
>When dealing with an integer do I use gets?
>What should I use instead of scanf??
>
>
You've been reading the wrong C books. Practical C programming 3rd
edition says to use fgets and sscanf instead of scanf to read numbers.
>BTW - can anyone refer me to a good algorithm for interpreting floats.
Use strtod, which is bullet-proof. For the algorithm, check P.J. Plauger's
book.
>I know the naive implementation can have some odd problems, but I'd
>like to see how they are
One obvious problem is floating point overflow. You have to detect it
*before* it happens.
> It is trivial to use scanf reliably, with no risk of undefined behaviour.
>
> >> >Friends do not let friends use *scanf() for numeric conversions.
> >>
> >> What did YOU use, back when you were a beginner still struggling with the
> >> C syntax and concepts?
In all seriousness, please post a code snippet for using scanf() or
fscanf() reliably for numeric conversions that is easier to explain to
a newbie than the combination of fgets() and strtol(), strtoul(), or
strtod().
Since you can't precheck the data stream with scanf() or (at least not
easily) with fscanf(), I would be interested in seeing trivial code
that extracted numeric values from these streams without the risk of
undefined behavior.
That's the reason I said "The first one is not especially elegant, nor
is it suited to all programming situations" about the getsafe()
function. It was written to give a newbie a safe alternative to gets.
They find using fgets() difficult due to the '\n' in the buffer that
must be removed before using the string in a call to fopen(), and
often messes up their printf() output by breaking a line.
One of these days I have to write a more robust replacement that
returns one of three int values, instead of char pointer, to the
caller:
0: Buffer had a '\n' that was found and removed.
1: Buffer did not contain a '\n', there is unread input in stdin.
-1: EOF encountered.
In cases where excessive input was entered, it is up to higher level
code to decide which of several possible approaches should be taken:
1. Discard what is in the string and the rest of the stream up to and
including '\n', issue an error to the user about improper input, and
prompt again.
2. Discard the remainder of the line from stdin and just use the
contents of the string (the truncated input).
3. Allocate a buffer or otherwise grab enough memory to continue
reading until the entire input line is put together.
4. Process the input line in pieces.
At different time, in different programs, I have used all of these
methods.
>Steve Horne wrote:
>>
>> On Tue, 31 Oct 2000 11:01:47 +0000, Richard Heathfield
>> wrote:
>>
>> >Steve Horne wrote:
>> >> Just because the input data is uninitialised, that does not mean it
>> >> doesn't have a value - merely that that value is (probably) not known
>> >> or correct.
>> >
>> >It's indeterminate. It may well have a trap representation. Or it may
>> >not. It's undefined.
>>
>> My initial thought - you cannot put a trap representation in a buffer
>> without the risk that it is coincedentally the same as some real input
>> data.
>
>The C programmer has no business "putting" a trap representation
>anywhere. What exists in a buffer before that buffer is initialised is
>indeterminate, so accessing it invokes undefined behaviour.
But the compiler or debugger may have. I wasn't thinking of the
programmer.
>> >> The function is not behaving in an undefined way.
>>
>> >The program is behaving in an undefined way. Therefore, the
>> >"definedness" of the function is undecidable.
>>
>> The part is not the whole. The function is a component of the program,
>> not the whole program. This is like claiming your windscreen is broken
>> just because your engine won't start.
>
>No, it isn't. A C program is not a car. It is a list of instructions to
>a C implementation. C implementations are under an obligation to ensure
>that a correct program is compiled correctly, but they have real world
>objectives too - marketing objectives, such as runtime speed, for
>example. This is recognised by the Standard, which basically implies
>"you do what you like, as long as it doesn't break correct programs".
>This is sometimes called the "as if" rule. Now, what works for correct
>programs may well not work for incorrect programs. Carefully crafted
>interdependencies within the object code could easily be broken in
>unexpected places by a rogue buffer. We do not have the right to assume
>otherwise. I have (vicariously) experienced the perils of leaving
>insufficient space for the null terminating character on a string -
>"Formatting drive C: - proceed (Y/N)?" (hey, this guy was lucky - he was
>asked). Undefined behaviour can set off pretty well anything the machine
>is physically capable of doing, including random emails to important
>people, for example (but no, I never saw that one actually happen...).
OK - Lets imagine Basil Fawlty hitting his car with a big stick
because his engine won't start. If he breaks the windscreen, that does
not mean that the windscreen is to blame. The root problem is still
the engine not starting (or maybe that Basil Fawlty needs to take a
pill). I don't see the value in blaming the windscreen.
>> After
>> all, by your definition, nothing in the universe is defined bar
>> (maybe) the universe as a whole, and the word 'defined' has no useful
>> meaning.
>
>I won't attempt to define "defined". Here's the definition of
>"undefined", as far as comp.lang.c is concerned:
>
<snip definition of 'undefined' from the FAQ>
OK, based on that we have a problem with the word 'defined'. It is
reminiscent of the ISO9000 definition of quality. If you or I buy a
crappy thing that breaks the first time we used it, we wouldn't
consider it a quality thing. Yet the manufacturer can claim the 'our
procedures are written down, and we followed them to the letter to
make that crappy thing, therefore it is quality'.
You can see how the ISO9000 definition of quality is only peripherally
related to the definition that English speakers use.
I didn't read much of the FAQ. It seemed to be telling me what I
already know. However, If I need to allow for it redefining the
English language to suit someones convenience, maybe I should.
>I think we might be arguing at cross-purposes. Let me give you an
>example of what I consider to be an appropriate level of error-checking,
>and you can see whether we do in fact agree. Here's the first part of an
>implementation of a function to populate an object stored in some
>dynamic data structure or other:
>
>#include <string.h>
>#include <assert.h>
>
>#include "foo.h"
>
>int RetrieveFromFoo(void *Object, size_t MaxSize, const FOO *Foo)
>{
> int Status = FOO_SUCCESS;
> assert(Object != NULL); /* program error */
> assert(Foo != NULL); /* program error */
> assert(Foo->Object != NULL; /* program error */
>
> if(MaxSize < Foo->Size)
> {
> Status = FOO_OBJECT_TOO_SMALL; /* data error */
> }
> else
> {
> memcpy(Object, Foo->Object, MaxSize); /* all okay */
> }
> return Status;
>}
>
>Now, I consider the amount of error processing in this function to be
>necessary and sufficient. If rogue pointers come in via Object or Foo,
>there's nothing I can do about it in terms of ISO C. I can debug, and
>fix elsewhere, but I can't programmatically handle that eventuality
>here. So I don't.
>
>Does Your Mileage Vary?
It very much depends on the context. Now lets see...
1. A safety critical application...
Standard asserts usually just bomb the application out. This is
unacceptable in safety critical applications, and undesirable
in general. In one safety critical application I worked on, the
solution was - on detecting an internal problem such as the above
- to backtrack to a previously known self consistent state and
restart from there. Should the restart fail or hit the same
problem again, switch to a seperate machine running on a different
architecture, written in a different language by a different
programming team.
2. Commercial application
Just bombing out is still unacceptable, but because halting the
processing is no longer worse than making-the-best-of-a-bad-job,
the assert should just record the failure and abort the current
thread. A highly trustworthy central core can manage threads and
report failures to users.
Similar handling can occur in non-threaded applications, but C
doesn't really provide the facilities (you end up with tonnes
of error-handling conditionals obscuring the actual code).
A critical thing here is to look at exacly what kinds of cleanup
are needed - eg have you just got part way through saving a
critical file which may therefore need to be recovered from a
.BAK file? Have you put hardware into a strange state that needs
to be reset?
3. Low level internal functions
Such paranoid checks can be excessive, especially in cases where
speed and code size are critical. This situation can occur in
small convenience functions hidden within libraries (ie not
callable from outside the library). Such libraries should only
be maintained by developers who are aware of the unsafe nature
of some of the code, and should be subject to even more intense
review and testing.
4. A first year college project
I would award you close to 100% - it is certainly a good base to
start from, and you should only deviate if you have good reason
to do so.
>> >> Deciding what error checks are necessary is not, therefore, a trivial
>> >> thing that can be handled with naive statements like 'all of them' -
>> >> there are conflicting issues that must be handled inteligently.
>> >
>> >[I love it when people misspell that word...]
>>
>> Why? - spelling ability and intelligence are not the same thing. In
>> fact, perfect spelling at all times could be seen as a sign of
>> obsessive-compulsive disorder.
>
>Alternatively, it could be seen as an indicator that the person
>considers correctness to be an important goal of a programmer. Anyway, I
>forgot the smiley, and didn't mean to get on your case about spelling.
>There are indeed more important things to discuss in comp.lang.c.
Trust me - I smiled, but I couldn't leave it at that ;-)
>> No - not an accusation - just something for ultra-pedants to chew on.
>
>My own view is that ultra-pedantry is an improvement on pedantry.
Do you have a problem with deadlines?
>> But - oh no! - ada has flaws too! Its syntax is wordier, often
>> obscuring the readability of code that is short and sweet in C, and
>> therefore making maintenance occasionally more difficult.
>
>I find that a sensible level of error-checking code does not get in the
>way of C's natural flair and elegance.
Never said it did - I was talking about ada syntax, not C error
checks. And it was a very nit-picking point at that.
>> Could it be that NOTHING IS PERFECT!!!
>
>No. My wife is perfect. :-)
LOL
>> <snip the first of several stupid mistakes by me from another thread>
>>
>> >I'm sure you'll agree with me that whoever wrote such code has a lot to
>> >learn about ISO C programming before he even thinks about taking on Dann
>> >Corbit in a debate on the subject.
>>
>> No - I simply made a human error. Of course if Dann Corbit is
>> infallible, then I have only one argument in favour of staying in the
>> debate...
>
>Dann Corbit is not infallible. I did not claim such a thing. Anyway,
>Dann would be the first (well, okay, maybe the third or fourth) to
>acknowledge that he is fallible.
Perhaps I should clarify : sometimes I resort to sarcasm.
>I was just pointing out that Dann knows a lot more about C than you or I
>do, so it might be wise to tread carefully.
Nah - if you never make a mistake, you never learn anything new.
>> In the meantime, I shall continue to contribute, hoping to learn more
>> myself and perhaps even raise something that someone else finds
>> useful.
>
>Good. Just go easy on the fights until you've done some more work on
>that left hook, okay? ;-)
I think I need to work more on running away ;-)
--
Steve Horne
s...@ttsoftware.co.uk
>Steve Horne wrote:
>> The usefulness of that check, though, is quite often limited because
>> whatever value you use to detect 'unchanged' could potentially be a
>> valid input value.
>
>Pass. Do you have any picture questions? ;-)
Erm - what do you mean by 'picture question'?
>I've had problems using atof() on mainframes. Perhaps you have
>experienced similar problems. I generally find strtod() does the right
>thing, though. If that doesn't turn the trick for you, either check
>Plauger ("The Standard C Library" - ISBN 0-13-131509-9) or ask Dann
>Corbit. :-)
Does Plauger include details of the algorithms used - a sort of 'how
to make your own standard library'? If so, it sounds very interesting.
--
Steve Horne
s...@ttsoftware.co.uk
>You've been reading the wrong C books. Practical C programming 3rd
>edition says to use fgets and sscanf instead of scanf to read numbers.
sscanf does not really allow for checking the string for errors. All
of the scanf functions assume to a large degree that the input string
matches the format string, and cope badly otherwise.
--
Steve Horne
s...@ttsoftware.co.uk
The compiler is under no obligation to initialise the values of
automatic variables unless the programmer explicitly requests it by
providing initialiser values. Therefore, the storage space allocated for
those variables might contain any old thing, including trap
representations. An example might be a function which calls two
functions - the first has a char * automatic variable, which is given
some address or other (legitimately). Naturally, once that variable has
done its job and the function goes out of scope, the variable goes out
of scope too. The memory it used is unlikely to be reinitialised by the
compiler (or at least, it's not obliged to be reinitialised). Now that
function calls the next function in line, which uses an automatic
variable. That variable happens to be an int, and happens to be using
the same memory as was originally used for that char *. A valid char *
value ain't necessarily a valid int value - it could be a trap
representation.
> >> >> The function is not behaving in an undefined way.
> >>
> >> >The program is behaving in an undefined way. Therefore, the
> >> >"definedness" of the function is undecidable.
> >>
> >> The part is not the whole. The function is a component of the program,
> >> not the whole program. This is like claiming your windscreen is broken
> >> just because your engine won't start.
> >
<snip explanation of interdependence>
>
> OK - Lets imagine Basil Fawlty hitting his car with a big stick
> because his engine won't start. If he breaks the windscreen, that does
> not mean that the windscreen is to blame. The root problem is still
> the engine not starting (or maybe that Basil Fawlty needs to take a
> pill). I don't see the value in blaming the windscreen.
<shrug> Proof by analogy is fraud. The situation is as I explained it
before. Yes, the root problem needs to be fixed. My point is very
simple: once undefined behaviour is invoked, the program is broken. All
of it.
<snip>
> >
> >I won't attempt to define "defined". Here's the definition of
> >"undefined", as far as comp.lang.c is concerned:
> >
> <snip definition of 'undefined' from the FAQ>
>
> OK, based on that we have a problem with the word 'defined'. It is
> reminiscent of the ISO9000 definition of quality. If you or I buy a
> crappy thing that breaks the first time we used it, we wouldn't
> consider it a quality thing. Yet the manufacturer can claim the 'our
> procedures are written down, and we followed them to the letter to
> make that crappy thing, therefore it is quality'.
And the manufacturer is right, within the terms of the Standard to which
he is working. Of course, he won't keep his customers for long, but
that's another issue.
>
> You can see how the ISO9000 definition of quality is only peripherally
> related to the definition that English speakers use.
Irrelevant. When discussing quality in terms of ISO9000, you use the
ISO9000 definition of quality. When discussing what behaviour is defined
and what behaviour is undefined within an ISO9899 program, you use the
ISO9899 definition of definition (IYSWIM).
>
> I didn't read much of the FAQ.
Oh dear. I suggest you remedy that as soon as possible.
> It seemed to be telling me what I
> already know.
If you'd read it more closely, it could have told you what you didn't
already know, and that could have saved you some upthread embarrassment.
> However, If I need to allow for it redefining the
> English language to suit someones convenience, maybe I should.
The Standard uses normal English meanings of words except where to do so
could lead to ambiguity, in which case it defines the terms it uses.
This is common sense, as far as I can see.
Ah, you have misunderstood assert(). Assertions validate a program, not
data. If the program is wrong, the assertion will fire. Once the program
is known to be correct, assertions are removed by defining NDEBUG. And
we have no business writing a safety-critical application without making
absolutely sure that the program is correct. assert() is one of the ways
in which this is done. It is not the only way, of course. assert() is
only remotely useful for /data/ validation if the function is guaranteed
by the design not to receive duff data, for example by a data validation
routine which is still under development. If the function can expect to
receive bad data, it should handle it gracefully, not bomb out.
>
> 2. Commercial application
>
> Just bombing out is still unacceptable, but because halting the
> processing is no longer worse than making-the-best-of-a-bad-job,
The program will not bomb out in production, because it will bomb out in
development and get fixed. The assertions simply will not be compiled
into the production build.
> the assert should just record the failure and abort the current
> thread. A highly trustworthy central core can manage threads and
> report failures to users.
Threads are not supported by either K&R C or ISO C, so they are
off-topic here.
<snip>
>
> 4. A first year college project
>
> I would award you close to 100%
How kind.
> - it is certainly a good base to
> start from, and you should only deviate if you have good reason
> to do so.
Please remember that I showed you an example, not a complete set of
techniques.
<snip>
> >> No - not an accusation - just something for ultra-pedants to chew on.
> >
> >My own view is that ultra-pedantry is an improvement on pedantry.
>
> Do you have a problem with deadlines?
Only when I forget to be pedantic early in the task. Early pedantry
saves me from late panic.
> >> But - oh no! - ada has flaws too! Its syntax is wordier, often
> >> obscuring the readability of code that is short and sweet in C, and
> >> therefore making maintenance occasionally more difficult.
> >
> >I find that a sensible level of error-checking code does not get in the
> >way of C's natural flair and elegance.
>
> Never said it did - I was talking about ada syntax, not C error
> checks. And it was a very nit-picking point at that.
But ada is off-topic here.
<snip>
>
> Perhaps I should clarify : sometimes I resort to sarcasm.
Oh. I thought I was the only one. Welcome to the club. I presume your
membership cheque is in the post?
> >> In the meantime, I shall continue to contribute, hoping to learn more
> >> myself and perhaps even raise something that someone else finds
> >> useful.
> >
> >Good. Just go easy on the fights until you've done some more work on
> >that left hook, okay? ;-)
>
> I think I need to work more on running away ;-)
In that case, you may be interested in my sideline in second-hand
Reeboks...
Does source code count as algorithms? If so, then yes, it includes
details of the algorithms used.
My god - I know all this. I was just putting in my thinking about how
it is *possible* and *allowed* for a compiler or debugger to put trap
representations into the generated executable (note the lack of words
like *obligated*), because you raised the subject but didn't explain
the relevance.
><shrug> Proof by analogy is fraud. The situation is as I explained it
>before. Yes, the root problem needs to be fixed. My point is very
>simple: once undefined behaviour is invoked, the program is broken. All
>of it.
You must have major problems debugging programs. If the whole thing is
broken, which bit are you supposed to fix? - or do you just rewrite
the whole thing from scratch.
Analogy is a major part of the way people think. It isn't a proof, but
people can understand the analogy and compare with the actual
situation to obtain a proof. It just bypasses the hysteria that people
have as soon as the subject is programming - logic works the same
regardless of the subject.
>> It seemed to be telling me what I
>> already know.
>
>If you'd read it more closely, it could have told you what you didn't
>already know, and that could have saved you some upthread embarrassment.
You are correct - It says one thing I didn't already know (that the
meaning of the word 'undefined' has been redefined). When you proove
that there is nothing in the FAQ that you have forgotten, I shall feel
guilty.
No - I am aware of this, but I strongly disaggree with the use of
NDEBUG. This means that what you are testing is not what your users
will be running. I have known several cases of people testing with
NDEBUG code, and...
1. Not catching all of the errors, through the impossibility of
testing all possible circumstances when the set of circumstances
is effectively (or, for real time, literally) infinite.
2. Because the use of NDEBUG code changes the behaviour of the
program sufficiently that it works in one case but not the other.
This can frequently occur due to timing issues and memory issues.
Therefore, NDEBUG should never be defined. I thought everyone knew
this.
Certainly an assert failure is a program error, but it is presumably
one that wasn't spotted during testing, and therefore only occurs with
certain data. The ability to recover from this is critical in a safety
critical application, as you cannot expect any program ever to be
completely perfect. This is why you have a backup system written by
different programmers and with different bugs - so they won't both
crash under the same circumstances.
However, if I follow your pattern of assuming-the-worst about my
knowledge, I can only assume you have never been trusted to write
safety critical code ;-)
I have - and I know exactly what is needed. The standard assert macro
does not cut the mustard, and I've never seen it used without being
redefined first.
>> 2. Commercial application
>>
>> Just bombing out is still unacceptable, but because halting the
>> processing is no longer worse than making-the-best-of-a-bad-job,
>
>The program will not bomb out in production, because it will bomb out in
>development and get fixed. The assertions simply will not be compiled
>into the production build.
You assume it is possible to test all combinations of inputs that can
occur. This is never possible in real systems. It is a basic tenet of
testing that there are bugs, and that you can never find them all. I
first learned this at age 14, at school - how about you?
>> the assert should just record the failure and abort the current
>> thread. A highly trustworthy central core can manage threads and
>> report failures to users.
>
>Threads are not supported by either K&R C or ISO C, so they are
>off-topic here.
It is very difficult to write any useful programs in K&R or ISO C
without any extensions. Threads are common, and not specific to any
platform. The unix fork has been discussed in a topic on this group
without any complaints I noticed.
>Only when I forget to be pedantic early in the task. Early pedantry
>saves me from late panic.
How very true. A standard piece of wisdom which managers seem to
refuse to accept.
Oh well.
>But ada is off-topic here.
What - you're never allowed to compare C with other languages?
--
Steve Horne
s...@ttsoftware.co.uk
>Steve Horne wrote:
>> Does Plauger include details of the algorithms used - a sort of 'how
>> to make your own standard library'? If so, it sounds very interesting.
>
>Does source code count as algorithms? If so, then yes, it includes
>details of the algorithms used.
Excellent - though my membership cheque for the sarcasm club might be
slightly delayed ;-)
--
Steve Horne
s...@ttsoftware.co.uk
Apologies. I misinterpreted what you said.
>
> ><shrug> Proof by analogy is fraud. The situation is as I explained it
> >before. Yes, the root problem needs to be fixed. My point is very
> >simple: once undefined behaviour is invoked, the program is broken. All
> >of it.
>
> You must have major problems debugging programs. If the whole thing is
> broken, which bit are you supposed to fix? - or do you just rewrite
> the whole thing from scratch.
Ah, you are extending my argument beyond my intent. All I meant was that
one cannot meaningfully discuss the behaviour of a program past the
point where it invokes undefined behaviour. (Example: in the case of
void main(), the behaviour becomes undefined even /before/ main() is
called! and thus it is not meaningful to discuss the behaviour of
programs which void main. The symptoms may, as I pointed out, arise in
some apparently unrelated part of the program - or they may not.)
> Analogy is a major part of the way people think. It isn't a proof, but
> people can understand the analogy and compare with the actual
> situation to obtain a proof. It just bypasses the hysteria that people
> have as soon as the subject is programming - logic works the same
> regardless of the subject.
<shrug> Perhaps if I had found your analogy to be meaningful, I would
have been more prepared to continue using it. I use analogy myself - not
as proof, but as illustration, just as you were trying to do. But if the
analogy doesn't work, to continue to try to use it is perhaps not
entirely helpful.
[FAQ]
> >> It seemed to be telling me what I
> >> already know.
> >
> >If you'd read it more closely, it could have told you what you didn't
> >already know, and that could have saved you some upthread embarrassment.
>
> You are correct - It says one thing I didn't already know (that the
> meaning of the word 'undefined' has been redefined). When you proove
> that there is nothing in the FAQ that you have forgotten, I shall feel
> guilty.
If you s/one thing/at least one thing/ then I agree.
It is because I forget things that are explained in the FAQ that I keep
a bound copy of it on my desk. I consult it regularly.
<snip demo code>
Sure. I've known people who don't bother to put assertions in their code
at all, who don't bother to check that they've got the return type of
functions correct, who don't bother to check error returns from fopen,
who don't bother to check for boundary conditions properly... And,
surprise surprise, their testing strategy is lame too. I don't see your
point, I'm afraid. Just because mistakes can happen, this does not mean
that we should not try to prevent mistakes, nor that we should berate
ourselves too heavily if, despite our best efforts, we occasionally
fail.
> 2. Because the use of NDEBUG code changes the behaviour of the
> program sufficiently that it works in one case but not the other.
> This can frequently occur due to timing issues and memory issues.
Therefore, you should not define NDEBUG when debugging, but you should
define NDEBUG when you are not debugging. Testing is not debugging. You
debug development code. You test production code. If the code is
correct, the program will withstand any data you throw at it; assertions
do not validate data. They validate code. The code should be valid by
the time it hits testing.
>
> Therefore, NDEBUG should never be defined. I thought everyone knew
> this.
NDEBUG should always be defined unless you are debugging. I thought
everyone knew this.
>
> Certainly an assert failure is a program error, but it is presumably
> one that wasn't spotted during testing, and therefore only occurs with
> certain data.
If it only occurs with certain data, it has no business being an
assertion failure. Assertions are to validate programs, not data.
> The ability to recover from this is critical in a safety
> critical application, as you cannot expect any program ever to be
> completely perfect. This is why you have a backup system written by
> different programmers and with different bugs - so they won't both
> crash under the same circumstances.
You mean - "so they are less likely to crash under the same
circumstances", I think.
>
> However, if I follow your pattern of assuming-the-worst about my
> knowledge, I can only assume you have never been trusted to write
> safety critical code ;-)
Well, I had some cause to assume-the-worst...
As it happens, I've never been in a position where I was even remotely
likely to write safety-critical code. That's probably a blessing for
society :-) but it's a bit like telling an eskimo "I assume you've never
been trusted to train a camel" - it just doesn't apply.
>
> I have -
That's frightening, given your apparent lack of knowledge of the C
programming language, as evinced by your silly upthread mistakes.
> and I know exactly what is needed. The standard assert macro
> does not cut the mustard, and I've never seen it used without being
> redefined first.
I think you're expecting the wrong things from it. With regard to its
intended purpose, it is necessary and sufficient.
>
> >> 2. Commercial application
> >>
> >> Just bombing out is still unacceptable, but because halting the
> >> processing is no longer worse than making-the-best-of-a-bad-job,
> >
> >The program will not bomb out in production, because it will bomb out in
> >development and get fixed. The assertions simply will not be compiled
> >into the production build.
>
> You assume it is possible to test all combinations of inputs that can
> occur.
You assume that the program is using assert() to validate data. It
isn't. It's validating the program.
> This is never possible in real systems. It is a basic tenet of
> testing that there are bugs, and that you can never find them all. I
> first learned this at age 14, at school - how about you?
I forget when I first learned it, I'm afraid. Of course you're right
that there are always bugs to be found, but the sort of bugs you think
assert() is for are not the sort of bugs assert() is actually for.
>
> >> the assert should just record the failure and abort the current
> >> thread. A highly trustworthy central core can manage threads and
> >> report failures to users.
> >
> >Threads are not supported by either K&R C or ISO C, so they are
> >off-topic here.
>
> It is very difficult to write any useful programs in K&R or ISO C
> without any extensions.
a) That isn't my experience. Extensions are a luxury I rarely enjoy. I
am accustomed to ISO C programming, and I do not find it terribly
difficult.
b) Whether it's difficult or not, it's what's topical here. Extensions
aren't.
> Threads are common, and not specific to any
> platform. The unix fork has been discussed in a topic on this group
> without any complaints I noticed.
How odd. fork()ers are usually fork()ed to comp.unix.programmer.
<snip>
>
> >But ada is off-topic here.
>
> What - you're never allowed to compare C with other languages?
Of course. It's a free planet. All you have to do is find the right
newsgroup in which to do it. comp.lang.c is not that newsgroup.
In article <9hvvvsk83gpie99n6...@4ax.com>
Steve Horne <s...@ttsoftware.co.uk> writes:
>sscanf does not really allow for checking the string for errors. All
>of the scanf functions assume to a large degree that the input string
>matches the format string, and cope badly otherwise.
To some extent, yes -- but everything is relative: while fgets+sscanf
may "cope badly" with errors, by comparison with a straight scanf
or fscanf, it copes quite well.
In particular, all of the scanf family always return the number of
assignments performed (modulo the exceptions for end-of-file or
input error, and %n "assignments"). This cannot tell you if there
was "extra junk", or whether some of the assignments overran their
array objects, or any other similar problem, but they *can* tell
you whether the input was so badly formed that not all of the
desired assignments were possible. Thus, for instance, the code:
/* fgets into "buf", then: */
if (sscanf(buf, "%d%d", &i1, &i2) != 2) {
... complain ...
} else {
... use i1 and i2 ...
}
will make sure that an input buffer began with two integer number
sequences. Input like "3 4" passes; input like "3 4 5 6" still
passes; and even "3 4 shut the door" passes. Input like "three
four" fails. Unlike straight scanf(), however, once the user has
typed in "three four" and pressed ENTER or RETURN (or clicked on
the "my input is ready" button or whatever it is that makes an
input line complete), the fgets+sscanf sequence *reads the entire
input line*[%], and if this code is in a loop, the next attempt
to read from the user moves on. A straight scanf() leaves the 't'
of "three" in the input stream, to jam up the machinery later.
-----
[%] Well, with the proviso that the buffer is big enough for the
entire line. Otherwise repeated calls to fgets(), or a sequence
of getc()s to consume up through the newline, are needed.
-----
In addition to behaving better in the face of "bad input", if only
by not gumming up the works for the rest of the code, the big
advantage of sscanf() is that, before and after it is done, you
still have the original input buffer. Since you have it *before*
the scanf(), you can use routines like strlen() to figure out how
much space the biggest "%s" or "%[" conversion could require.
Finally, if you *do* want to check for "trailing junk", sscanf()
makes it possible. Suppose the buffer being scanned has length
"len", and you do want to scan two "int"s i1 and i2. Then:
size_t len;
long scanlen;
...
len = strlen(buf);
n = sscanf(buf, "%d%d %ln", &i1, &i2, &scanlen);
if (n != 2) ... the buffer did not begin with two "int"s
if (scanlen != len) ... the buffer had junk after the "int"s
The "%n" directive, if it is ever reached, assigns the number of
characters scanned-or-skipped-so-far to the next va_arg(). (I use
a "long" here in case size_t is unsigned long -- there is no unsigned
version of %n, and even though a negative value is impossible, for
some reason %n produces "int" and %ln produces "long int". So
there is still a flaw here: if long is only 32 bits and len exceeds
2147483647, this could go awry.) If n is 2, both "%d" conversions
succeeded, so there are at least two subject sequences that match
"int" at the beginning of the buffer. The white-space directive
after the second %d can never fail, even in the face of EOF or
error, so the following %n conversion must also succeed and set
"scanlen". After skipping any optional trailing white space (blanks,
tabs, newlines, etc.) that follow the second integer, the scanf()
call should reach the end of the input string -- "EOF", as it were.
In this case, scanlen should be equal to len. If scanlen is less
than len -- and no other case is possible -- there must have been
"trailing junk" after the two integers.
(Note that you must not test scanlen if n < 2 -- in that case, the
%ln directive was never reached. You could set scanlen to 0 before
starting, but you still have to check n anyway.)
This technique does not work with plain scanf, and in general cannot
be made to work, because (e.g.) "%d" directives skip leading white
space, including entirely-blank lines. Even when scanning with
conversions like %c and %[, the equivalent of the "trailing blank
and %n directive" turns out to be rather klunky:
int saw_trailing_junk = 0, c;
while ((c = getc(fp)) != EOF && c != '\n')
if (!isspace(c))
saw_trailing_junk = 1;
One can argue that the %n directive technique is also klunky, but
at least it can be done as a single line:
if (n != number_of_directives || scanlen != len) {
... complain about bad input in buffer ...
} else {
... proceed with converted input ...
}
The less effort one has to put into error handling, the more likely
one is to do it at all, and careful use of fgets+sscanf makes
handling the most-typical, most-common errors fairly easy. Direct
use of scanf or fscanf makes it quite difficult.
(On the other hand, perhaps c.l.c should take a leaf from sfio,
and invent a new, well-behaved function for reading from stdio
streams.)
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA, USA Domain: to...@bsdi.com +1 510 234 3167
http://claw.bsdi.com/torek/ (not always up) I report spam to abuse@.
>In all seriousness, please post a code snippet for using scanf() or
>fscanf() reliably for numeric conversions that is easier to explain to
>a newbie than the combination of fgets() and strtol(), strtoul(), or
>strtod().
>
>Since you can't precheck the data stream with scanf() or (at least not
>easily) with fscanf(), I would be interested in seeing trivial code
>that extracted numeric values from these streams without the risk of
>undefined behavior.
#include <stdio.h>
int main()
{
int i;
char c;
fprintf(stderr, "Enter a number between 0 and 9999: ");
fflush(stderr);
if (scanf("%4d%c", &i, &c) < 2 || c != '\n' || i < 0)
printf("You're a bad boy\n");
else printf("Good boy, you've entered: %d\n", i);
return 0;
}
This is probably too sophisticated for a newbie, who only needs to do
something like:
if (scanf("%4d", &i) < 1) { /* complain about bad input and exit */ }
and still get a bullet-proof program.
The newbie only writes simple programs that are meant to be used
exclusively by himself, to test that they do what they're supposed to do.
If you keep this in mind, even scanf("%d", &i) is good enough for the
job, because the newbie *knows* what kind of data the program expects
from him.
To use strto* to their full capabilities, you need to introduce the
concept of pointers to pointers, which is not exactly newbie friendly,
while scanf doesn't even need the concept of pointer, you simply state
that it needs the funny & chars in front of its numeric arguments for
reasons that will be explained later.
>Steve Horne wrote:
>>
>> On Wed, 01 Nov 2000 12:02:26 +0000, Richard Heathfield
>> <ric...@antlimited.com> wrote:
>>
>> >> It seemed to be telling me what I
>> >> already know.
>> >
>> >If you'd read it more closely, it could have told you what you didn't
>> >already know, and that could have saved you some upthread embarrassment.
>>
>> You are correct - It says one thing I didn't already know (that the
>> meaning of the word 'undefined' has been redefined). When you proove
>> that there is nothing in the FAQ that you have forgotten, I shall feel
>> guilty.
>
>If you s/one thing/at least one thing/ then I agree.
Maybe.
>It is because I forget things that are explained in the FAQ that I keep
>a bound copy of it on my desk. I consult it regularly.
Have you consulted the bit in the style section that says rules are
just guidelines?
>> No - I am aware of this, but I strongly disaggree with the use of
>> NDEBUG. This means that what you are testing is not what your users
>> will be running. I have known several cases of people testing with
>> NDEBUG code, and...
>>
>> 1. Not catching all of the errors, through the impossibility of
>> testing all possible circumstances when the set of circumstances
>> is effectively (or, for real time, literally) infinite.
>
>Sure. I've known people who don't bother to put assertions in their code
>at all, who don't bother to check that they've got the return type of
>functions correct, who don't bother to check error returns from fopen,
>who don't bother to check for boundary conditions properly... And,
>surprise surprise, their testing strategy is lame too. I don't see your
>point, I'm afraid. Just because mistakes can happen, this does not mean
>that we should not try to prevent mistakes, nor that we should berate
>ourselves too heavily if, despite our best efforts, we occasionally
>fail.
Re-examine the example - it suggests much MORE checking than you did.
>
>> 2. Because the use of NDEBUG code changes the behaviour of the
>> program sufficiently that it works in one case but not the other.
>> This can frequently occur due to timing issues and memory issues.
>
>Therefore, you should not define NDEBUG when debugging, but you should
>define NDEBUG when you are not debugging. Testing is not debugging. You
>debug development code. You test production code. If the code is
>correct, the program will withstand any data you throw at it; assertions
>do not validate data. They validate code. The code should be valid by
>the time it hits testing.
No - I have known programs work during testing but fail in the real
world, purely because someone took out the NDEBUG for the real world
release.
In one case, the reason was that slowing down a critical section
allowed the program to work. Removing assertions and other checks
allowed that section to run faster, and thus create a timing issue
that caused the whole thing to crash.
Code is written by humans. The peers that review it are humans. The
people who design the tests are humans. In a complex system, a few
bugs get past all of them. Aircraft pilots are not happy when they die
just because someone thought it was unreasonable to expect their
programs to cope with the existence of programming bugs.
I have written software for military aircraft in C, among other
things. It hasn't killed anyone.
>> Therefore, NDEBUG should never be defined. I thought everyone knew
>> this.
>
>NDEBUG should always be defined unless you are debugging. I thought
>everyone knew this.
No - see above. Testing something other than what you give the users
is daft. Of course unit testing with coverage statistics etc breaks
this rule by necessity (ie testing the units in the context of a
testing system rather than the real program) but this should be in
addition to real application testing, and it should still be the same
unit that will be in the real user-distributed application. No messing
around with NDEBUG.
The idea that you know x works because you've tested y is insane.
>> Certainly an assert failure is a program error, but it is presumably
>> one that wasn't spotted during testing, and therefore only occurs with
>> certain data.
>
>If it only occurs with certain data, it has no business being an
>assertion failure. Assertions are to validate programs, not data.
I'm not saying its validating data - read what I'm saying. Some
program errors only occur in certain situations - certain states,
certain inputs. These can be absolutely valid states and inputs, but
due to a program error they cause a problem. In an ideal fantasy world
you could disreguard this and say that there will be no program
errors, but I live in the real world and I'm not willing to bet my
life or anyone elses on a fantasy.
>> The ability to recover from this is critical in a safety
>> critical application, as you cannot expect any program ever to be
>> completely perfect. This is why you have a backup system written by
>> different programmers and with different bugs - so they won't both
>> crash under the same circumstances.
>
>You mean - "so they are less likely to crash under the same
>circumstances", I think.
Typically much less likely. Just because the technique isn't perfect,
it doesn't mean it shouldn't be used. Put it this way.
Imagine you are about to make a transatlantic flight. You are being
offered a choice to go in one of two aircraft.
The first has the slogan - we've tested this, so it couldn't possibly
go wrong. We don't acknowledge the possibility of error, so why should
we prepare for it.
The second has the slogan - we've tested this just as much as the
other one, but we have means to detect and attempt to recover from
errors as well, and furthermore we have backup systems just in case.
Which would you choose?
>> However, if I follow your pattern of assuming-the-worst about my
>> knowledge, I can only assume you have never been trusted to write
>> safety critical code ;-)
>
>Well, I had some cause to assume-the-worst...
>
>As it happens, I've never been in a position where I was even remotely
>likely to write safety-critical code. That's probably a blessing for
>society :-) but it's a bit like telling an eskimo "I assume you've never
>been trusted to train a camel" - it just doesn't apply.
>
>>
>> I have -
>
>That's frightening, given your apparent lack of knowledge of the C
>programming language, as evinced by your silly upthread mistakes.
I thought only your wife was perfect?
Mistakes happen. You yourself berated me for my spelling, then made a
spelling mistake yourself. The critical thing is to do everything
possible to ensure that the mistakes you make cause no damage.
Besides, the real reason for those mistakes (which were not upthread,
but in a different thread) was that just ATM, C is not my main
language. I have professionally used over a dozen languages, and at
the moment C is a relatively low concern. In any context other than a
usenet post, I'd have taken a little more time and would (probably)
not have made those mistakes.
>> and I know exactly what is needed. The standard assert macro
>> does not cut the mustard, and I've never seen it used without being
>> redefined first.
>
>I think you're expecting the wrong things from it. With regard to its
>intended purpose, it is necessary and sufficient.
It is sufficient to change the thing you are testing to be different
from what the user is expected to trust. It is not necessary for that,
as there are other ways to acheive the same thing.
It is also not necessary as a way of validating your code. There are
many better ways - including the use of an implementation of asserts
that stays in your release code.
>> >> 2. Commercial application
>> >>
>> >> Just bombing out is still unacceptable, but because halting the
>> >> processing is no longer worse than making-the-best-of-a-bad-job,
>> >
>> >The program will not bomb out in production, because it will bomb out in
>> >development and get fixed. The assertions simply will not be compiled
>> >into the production build.
>>
>> You assume it is possible to test all combinations of inputs that can
>> occur.
>
>You assume that the program is using assert() to validate data. It
>isn't. It's validating the program.
Covered above. Programs fail in particular circumstances. A program
that always fails should never make it to the user. A program that
ALMOST never fails will very likely make it to the user.
Valid inputs sometimes have erroneous side effects.
>> This is never possible in real systems. It is a basic tenet of
>> testing that there are bugs, and that you can never find them all. I
>> first learned this at age 14, at school - how about you?
>
>I forget when I first learned it, I'm afraid. Of course you're right
>that there are always bugs to be found, but the sort of bugs you think
>assert() is for are not the sort of bugs assert() is actually for.
Yes they are - programming errors which haven't been detected and
fixed. You seem to assume that it is possible to detect and fix all
programming errors before release. Explain that to a fighter pilot
just before he goes on a mission, and you might just have less time to
live than he does.
>> It is very difficult to write any useful programs in K&R or ISO C
>> without any extensions.
>
>a) That isn't my experience. Extensions are a luxury I rarely enjoy. I
>am accustomed to ISO C programming, and I do not find it terribly
>difficult.
Then you are not writing desktop applications, and neither are you
writing embedded systems. All your I/O is either file based or very
simple terminal-style user interaction. I'd be half impressed if you
are writing a compiler, but even so, according to this statement, your
experience has definite limits.
>> >But ada is off-topic here.
>>
>> What - you're never allowed to compare C with other languages?
>
>Of course. It's a free planet. All you have to do is find the right
>newsgroup in which to do it. comp.lang.c is not that newsgroup.
So future revisions of the C language cannot possibly have anything to
learn from other languages?
Don't be daft. Every comp.lang.* group I've ever contributed to has
debates comparing its language to others. Such debates often
contribute to the future development of those languages.
My mention of ada was an answer to an issue raised by you. Please stop
declaring any reply you don't like to be off-topic. Or else I will
declare all replys written in any human language to be off topic, and
refuse to listen ;-)
--
Steve Horne
s...@ttsoftware.co.uk
Choices!
A friend was at Peking Airport waiting for a flight to Lhasa when the
following announcement came over the PA.
Bing bong
"Would passengers for Chinese Airlines flight to Lhasa please note that
plane is sick. We get new plane."
Bing bong
An hour or so later the followup announcement was made:
Bing bong
"Would passengers for Chinese Airlines flight to Lhasa please note that we
get new plane. But new plane is more sick than first plane so we take first
plane."
Bing bong
You don't necessarily have a choice.
As for using assert: At the very least the effect of its removal from a real
time system should be checked for timing errors before the software is
released. If timing is that crucial then more thought should have been given
at the design stage.
--
Bob Wightman
Work is just a screensaver
Views expressed in this message are my own and not those of my employer.
>In particular, all of the scanf family always return the number of
>assignments performed (modulo the exceptions for end-of-file or
>input error, and %n "assignments"). This cannot tell you if there
>was "extra junk", or whether some of the assignments overran their
>array objects, or any other similar problem, but they *can* tell
>you whether the input was so badly formed that not all of the
>desired assignments were possible. Thus, for instance, the code:
>
> /* fgets into "buf", then: */
> if (sscanf(buf, "%d%d", &i1, &i2) != 2) {
> ... complain ...
> } else {
> ... use i1 and i2 ...
> }
<snip various other useful sscanf tips>
I didn't know this, so thanks. But I'd still want to do better in
most, if not all, applications.
Of course, the checks sscanf does here would make it simpler than I
realised to do a pre-check before the sscanf. The trouble is, the
things its easy to pre-check (token types, valid characters) are the
same things sscanf can check already.
>The less effort one has to put into error handling, the more likely
>one is to do it at all, and careful use of fgets+sscanf makes
>handling the most-typical, most-common errors fairly easy. Direct
>use of scanf or fscanf makes it quite difficult.
I agree with this principle, but most applications should have fairly
centralised I/O and file access sections anyway, so creating some
specialised parse-and-error-check routines, while non-trivial, is
usually but not a great hardship.
>(On the other hand, perhaps c.l.c should take a leaf from sfio,
>and invent a new, well-behaved function for reading from stdio
>streams.)
SFIO certainly looks promissing at first glance, but I have that sense
that I'm looking at another 100-bladed swiss army knife ;-)
--
Steve Horne
s...@ttsoftware.co.uk
Yes. It says:
Most observations or "rules" about programming style (Structured
Programming is Good, gotos are Bad, functions should fit on one page,
etc.) usually work better as guidelines than as rules and work much
better if programmers understand what the guidelines are trying to
accomplish. Blindly avoiding certain constructs or following rules
without understanding them can lead to just as many problems as the
rules were supposed to avert.
(Note: Steve specifically relates this to style issues, not correctness
issues. Check the first sentence again, in words 4 through 7.)
I don't have a problem with that paragraph.
>
> >> No - I am aware of this, but I strongly disaggree with the use of
> >> NDEBUG. This means that what you are testing is not what your users
> >> will be running. I have known several cases of people testing with
> >> NDEBUG code, and...
> >>
> >> 1. Not catching all of the errors, through the impossibility of
> >> testing all possible circumstances when the set of circumstances
> >> is effectively (or, for real time, literally) infinite.
> >
> >Sure. I've known people who don't bother to put assertions in their code
> >at all, who don't bother to check that they've got the return type of
> >functions correct, who don't bother to check error returns from fopen,
> >who don't bother to check for boundary conditions properly... And,
> >surprise surprise, their testing strategy is lame too. I don't see your
> >point, I'm afraid. Just because mistakes can happen, this does not mean
> >that we should not try to prevent mistakes, nor that we should berate
> >ourselves too heavily if, despite our best efforts, we occasionally
> >fail.
>
> Re-examine the example - it suggests much MORE checking than you did.
Show me how the function I showed you could be re-coded, given no more
information about the inputs than is implicit in the code already
present, so that more error-checking is done. Here's the code again:
#include <string.h>
#include <assert.h>
#include "foo.h"
int RetrieveFromFoo(void *Object, size_t MaxSize, const FOO *Foo)
{
int Status = FOO_SUCCESS;
assert(Object != NULL); /* program error */
assert(Foo != NULL); /* program error */
assert(Foo->Object != NULL; /* program error */
if(MaxSize < Foo->Size)
{
Status = FOO_OBJECT_TOO_SMALL; /* data error */
}
else
{
memcpy(Object, Foo->Object, MaxSize); /* all okay */
}
return Status;
}
And here's my attempt at making it even stricter:
#include <string.h>
#include <assert.h>
#include "foo.h"
int RetrieveFromFoo(void *Object, size_t MaxSize, const FOO *Foo)
{
int Status = FOO_SUCCESS;
assert(Object != NULL); /* program error */
assert(Foo != NULL); /* program error */
assert(Foo->Object != NULL; /* program error */
if(Foo == NULL)
{
Status = FOO_ARG_IS_NULL;
}
else if(Foo->Object == NULL)
{
Status = FOO_NO_OBJECT;
}
else if(Object == NULL)
{
Status = FOO_NO_INPUT;
}
else if (MaxSize < Foo->Size)
{
Status = FOO_OBJECT_TOO_SMALL; /* data error */
}
else
{
memcpy(Object, Foo->Object, MaxSize); /* all okay */
}
return Status;
}
Note that this extra code needlessly complicates the function if, as I
was implying in the original code, the various assertion checks assert
that the program is correct (i.e. that this function is called only by
functions which know not to pass it lousy data). If the design calls for
a function which must expect lousy data (e.g. a data validation
module!), then assertions are not appropriate, and I wouldn't claim that
they were.
> >> 2. Because the use of NDEBUG code changes the behaviour of the
> >> program sufficiently that it works in one case but not the other.
> >> This can frequently occur due to timing issues and memory issues.
> >
> >Therefore, you should not define NDEBUG when debugging, but you should
> >define NDEBUG when you are not debugging. Testing is not debugging. You
> >debug development code. You test production code. If the code is
> >correct, the program will withstand any data you throw at it; assertions
> >do not validate data. They validate code. The code should be valid by
> >the time it hits testing.
>
> No - I have known programs work during testing but fail in the real
> world, purely because someone took out the NDEBUG for the real world
> release.
No, it wasn't purely for that reason. It was purely because there was a
bug in the code. The fact that taking NDEBUG out revealed a flaw
indicates to me that assert() (or #ifdef NDEBUG, or whatever the code
did) was not being used in an appropriate way.
>
> In one case, the reason was that slowing down a critical section
> allowed the program to work. Removing assertions and other checks
> allowed that section to run faster, and thus create a timing issue
> that caused the whole thing to crash.
If a program only works when its development scaffolding is in place,
then it isn't yet ready for the Real World.
>
> Code is written by humans. The peers that review it are humans. The
> people who design the tests are humans. In a complex system, a few
> bugs get past all of them. Aircraft pilots are not happy when they die
> just because someone thought it was unreasonable to expect their
> programs to cope with the existence of programming bugs.
How do you know? :-)
Er... moving swiftly on...
If I were a pilot and got killed by a void main program deciding to dump
my fuel over Shropshire at 30000 feet and 600mph, I'd be bloody annoyed.
>
> I have written software for military aircraft in C, among other
> things. It hasn't killed anyone.
Well, there's a mercy.
>
> >> Therefore, NDEBUG should never be defined. I thought everyone knew
> >> this.
> >
> >NDEBUG should always be defined unless you are debugging. I thought
> >everyone knew this.
>
> No - see above. Testing something other than what you give the users
> is daft.
I agree. Can't you read? Testing is what you do to production code.
NDEBUG is not defined during development - it's defined during testing.
Therefore, what you test is non-assert code, and what you give to the
users is non-assert code. Assertions are for programmers, not for
testers.
> Of course unit testing with coverage statistics etc breaks
> this rule by necessity (ie testing the units in the context of a
> testing system rather than the real program) but this should be in
> addition to real application testing, and it should still be the same
> unit that will be in the real user-distributed application. No messing
> around with NDEBUG.
No, that's right - don't mess about with it. Don't do anything at all
with it, in fact, except define it. The C implementation will take care
of the rest for you.
>
> The idea that you know x works because you've tested y is insane.
Right. The idea that you know x works because you've tested x is insane
too, but it's less insane.
All you prove by testing x is either that x doesn't work, or that you're
not sure.
But you seem to be thinking that I advocate testing with NDEBUG not
defined, and shipping with NDEBUG defined, and I am not advocating that
at all. I advocate testing with NDEBUG defined and shipping with NDEBUG
defined. Assertions are for programmers.
>
> >> Certainly an assert failure is a program error, but it is presumably
> >> one that wasn't spotted during testing, and therefore only occurs with
> >> certain data.
> >
> >If it only occurs with certain data, it has no business being an
> >assertion failure. Assertions are to validate programs, not data.
>
> I'm not saying its validating data - read what I'm saying. Some
> program errors only occur in certain situations - certain states,
> certain inputs. These can be absolutely valid states and inputs, but
> due to a program error they cause a problem. In an ideal fantasy world
> you could disreguard this and say that there will be no program
> errors, but I live in the real world and I'm not willing to bet my
> life or anyone elses on a fantasy.
I would just love to see an example. Not that it would help the debate,
because any example you give can be countered with a "don't use assert()
in that situation", either honestly or dishonestly. Even though I can
protest that I'd be impartial in assessing the example, you should treat
all such claims on Usenet with extreme suspicion until you've worked out
who's who, so to speak.
That's the trouble, you see - I think you're using assert() in the wrong
way, and that this is why you daren't define NDEBUG; and you think that
the way you're using assert() is correct, and any example you show me is
therefore likely to be the subject of controversy between us.
For the record, I live in the real world too.
>
> >> The ability to recover from this is critical in a safety
> >> critical application, as you cannot expect any program ever to be
> >> completely perfect. This is why you have a backup system written by
> >> different programmers and with different bugs - so they won't both
> >> crash under the same circumstances.
> >
> >You mean - "so they are less likely to crash under the same
> >circumstances", I think.
>
> Typically much less likely. Just because the technique isn't perfect,
> it doesn't mean it shouldn't be used.
I agree that it's typically much less likely, and I agree the technique
can be used even if it isn't perfect. I was merely pointing out that
there is no guarantee that they won't crash - you had made a categorical
statement incorrectly, that's all.
> Put it this way.
>
> Imagine you are about to make a transatlantic flight. You are being
> offered a choice to go in one of two aircraft.
>
> The first has the slogan - we've tested this, so it couldn't possibly
> go wrong. We don't acknowledge the possibility of error, so why should
> we prepare for it.
>
> The second has the slogan - we've tested this just as much as the
> other one, but we have means to detect and attempt to recover from
> errors as well, and furthermore we have backup systems just in case.
>
> Which would you choose?
The one with the tastiest in-flight meals and the most legroom, to be
honest with you. :-)
Okay, naturally I'd choose the second. But you see, you have
misunderstood me. I think error-checking is extremely important, and
run-time error checking is vital. So is error recovery. So are backup
systems.
I'll tell you what's /not/ important, though - superstition. Consider
this code:
const int a = 5;
const int b = 6;
int c = a + b;
if(c != a + b)
{
/* what? */
This if() is pointless. Agree? Disagree?
Do you agree that the only way this test is going to fail (bearing in
mind that c is not volatile) is if the processor is fscked? And, if it
is, do you trust it to do the if() correctly?
Consider, now, the following translation unit:
#include "foo.h"
static int get_foo_bar(FOO *foo)
{
assert(foo != NULL);
return foo->bar;
}
int firewall_foo_function(FOO *foo)
{
int rc;
if(foo == NULL)
{
rc = INVALID_FOOBAR;
}
else
{
rc = get_foo_bar(foo);
}
return rc;
}
/* end of translation unit */
Are you seriously telling me that you'd leave the assertion in place in
production code?
If the program design is such that a function cannot receive bad data, I
am happy to assert that the data are correct. If the function might
receive bad data, then it's not an assertion situation.
>
> >> However, if I follow your pattern of assuming-the-worst about my
> >> knowledge, I can only assume you have never been trusted to write
> >> safety critical code ;-)
> >
> >Well, I had some cause to assume-the-worst...
> >
> >As it happens, I've never been in a position where I was even remotely
> >likely to write safety-critical code. That's probably a blessing for
> >society :-) but it's a bit like telling an eskimo "I assume you've never
> >been trusted to train a camel" - it just doesn't apply.
> >
> >>
> >> I have -
> >
> >That's frightening, given your apparent lack of knowledge of the C
> >programming language, as evinced by your silly upthread mistakes.
>
> I thought only your wife was perfect?
Sure. But there's imperfect and then there's dumb. I'm not saying
/you're/ dumb - but that code was. I'd better stop going on about it
before you get thoroughly annoyed by it, but please remember that void
main is the mark of a newbie in comp.lang.c, okay? (In other words, it's
the mark of someone who hasn't yet learned that the language is defined
not by their compiler or by what seems to work, but by the ISO C
Standard.) People who continue not to understand this graduate from
newbie to lamer real fast.
> Mistakes happen. You yourself berated me for my spelling, then made a
> spelling mistake yourself.
Really? Citations, please, for both the berating and the spelling
mistake.
> The critical thing is to do everything
> possible to ensure that the mistakes you make cause no damage.
>
> Besides, the real reason for those mistakes (which were not upthread,
> but in a different thread)
Good spot. :-) See? I told you I wasn't perfect.
> was that just ATM, C is not my main
> language. I have professionally used over a dozen languages, and at
> the moment C is a relatively low concern. In any context other than a
> usenet post, I'd have taken a little more time and would (probably)
> not have made those mistakes.
Interesting. It's a shame, though - if you'd thought a little more, and
perhaps read the group for a while before posting, you could have got a
much better start here, because you'd never have posted that void main
program.
>
> >> and I know exactly what is needed. The standard assert macro
> >> does not cut the mustard, and I've never seen it used without being
> >> redefined first.
> >
> >I think you're expecting the wrong things from it. With regard to its
> >intended purpose, it is necessary and sufficient.
>
> It is sufficient to change the thing you are testing to be different
> from what the user is expected to trust. It is not necessary for that,
> as there are other ways to acheive the same thing.
Ah, no, you see, we disagree about when assert() should be used.
>
> It is also not necessary as a way of validating your code. There are
> many better ways - including the use of an implementation of asserts
> that stays in your release code.
Assertions, when they fire, show the programmer that the code is
incorrect. If the code is correct, the assertions won't fire. If the
code is incorrect, why is it shipping? Assertion code is debug code, not
the code you test and put into production. If your program design is
such that a pointer *cannot* be NULL, assert that it is not NULL. If
your program design is such that a pointer might be NULL, and that this
would be a Bad Thing, then test to see whether it's NULL and return an
error if it is.
>
> >> >> 2. Commercial application
> >> >>
> >> >> Just bombing out is still unacceptable, but because halting the
> >> >> processing is no longer worse than making-the-best-of-a-bad-job,
> >> >
> >> >The program will not bomb out in production, because it will bomb out in
> >> >development and get fixed. The assertions simply will not be compiled
> >> >into the production build.
> >>
> >> You assume it is possible to test all combinations of inputs that can
> >> occur.
> >
> >You assume that the program is using assert() to validate data. It
> >isn't. It's validating the program.
>
> Covered above. Programs fail in particular circumstances. A program
> that always fails should never make it to the user. A program that
> ALMOST never fails will very likely make it to the user.
I think I really would like to see how you use assert(). I'm beginning
to think it's like this:
assert(fgets(data, sizeof data, stdin) != NULL);
!!!!!!!!
>
> Valid inputs sometimes have erroneous side effects.
Assertion doesn't deal with inputs (i.e. runtime data) but with program
constructs.
<snip>
> >> It is very difficult to write any useful programs in K&R or ISO C
> >> without any extensions.
> >
> >a) That isn't my experience. Extensions are a luxury I rarely enjoy. I
> >am accustomed to ISO C programming, and I do not find it terribly
> >difficult.
>
> Then you are not writing desktop applications, and neither are you
> writing embedded systems. All your I/O is either file based or very
> simple terminal-style user interaction. I'd be half impressed if you
> are writing a compiler, but even so, according to this statement, your
> experience has definite limits.
Of course my experience has definite limits, you stupid man. So does
everyone's.
If you wish to indulge in ad hominum arguments, however, go pick a fight
with someone else. In comp.lang.c, I am prepared to discuss the C
programming language, rationally, with you and whoever else wants to
discuss the subject, with the discussion being based firmly on the
Standard. If I write some code which you can demonstrate is wrong
because the Standard says it's wrong, then flame me all you want, but I
am not prepared to indulge in fatuous insult-trading about lack of
experience which has no basis in fact or in the Standard.
>
> >> >But ada is off-topic here.
> >>
> >> What - you're never allowed to compare C with other languages?
> >
> >Of course. It's a free planet. All you have to do is find the right
> >newsgroup in which to do it. comp.lang.c is not that newsgroup.
>
> So future revisions of the C language cannot possibly have anything to
> learn from other languages?
Of course they can. But comp.lang.c discusses the current and previous
Standards. For future direction, you want comp.std.c.
>
> Don't be daft. Every comp.lang.* group I've ever contributed to has
> debates comparing its language to others. Such debates often
> contribute to the future development of those languages.
This isn't comp.lang.*; it's comp.lang.c.
> My mention of ada was an answer to an issue raised by you.
What has that to do with anything? If the answer is off-topic, don't
give that answer. Find one that's on-topic.
> Please stop
> declaring any reply you don't like to be off-topic.
I have not done so. I have declared off-topic replies to be off-topic.
There is a difference.
> Or else I will
> declare all replys written in any human language to be off topic, and
> refuse to listen ;-)
It is your right to refuse to listen, but not within your power to
change the topic of comp.lang.c without persuading a majority of
subscribers to agree with you.
> I have written software for military aircraft in C, among other
> things. It hasn't killed anyone.
I thought the idea was to kill *someone*
:-)
Sent via Deja.com http://www.deja.com/
Before you buy.
This is an excellent book. It basically implements the complete
standard library. It isn't completly portable, though it aims for a
very high degree of portability. Plauger was the library expert on the
ANSI commitee.
Reading this book teaches a lot about the standard library, about
programming in C and about library design in general.
Truly awesome.
--
Dan Pop: "When was the last time you've implemented a real life
application as a strictly conforming program?"
Richard Heathfield: "About 20 minutes ago. It was a new, heavily
optimised pig-launching routine, which gets us a 70% range increase on
previous porcine aeronautic programs."
The input string is NOT assumed to match the format string: at the
first mismatch the function stops converting and returns the number of
input items assigned so far.
The conversion specifiers include safety devices against overflow, so
the function can be used for bullet-proof user input (except for floating
point data, where the maximum field width is not adequate for overflow
protection).
> Therefore, NDEBUG should never be defined. I thought everyone knew
> this.
Yes, it should. It's quite simple: if you ever, _ever_, inflict an
assert failure on one of my users, I will do you physical harm. It is
simply not something that a user should ever be able to see. An assert
failure during user run time means that the program wasn't fit for prime
time yet, and that programmer _and_ quality control failed big time.
If you don't understand the common user well enough to know that assert
failures are too technical for them to handle, you should be programming
in VBA, not in C.
Richard
>In article <ei900tse33aqkg0j4...@4ax.com>,
> Steve Horne <s...@ttsoftware.co.uk> wrote:
>
>> I have written software for military aircraft in C, among other
>> things. It hasn't killed anyone.
>
>I thought the idea was to kill *someone*
The maker of the gun is not the killer - the guy who pulls the trigger
(or perhaps the guy who tells him to) is.
Besides, I never worked on weapons systems. Well, OK, once - but that
was just a test rig anyway.
--
Steve Horne
s...@ttsoftware.co.uk
> In article <ei900tse33aqkg0j4...@4ax.com>,
> Steve Horne <s...@ttsoftware.co.uk> wrote:
>
> > I have written software for military aircraft in C, among other
> > things. It hasn't killed anyone.
>
> I thought the idea was to kill *someone*
>
> :-)
Nick...
This is why the issue of /scope/ is so important.
One would hope that none of the people /inside/ (or exiting from) the
aircraft would be killed by the plane's behavior.
There's some fairly "gee-whiz" software in some of these "fly by wire"
birds that has kept them flying (and controllable) after being
significantly damaged -- such as after losing half a wing, etc. I have
to admit that, although I abhor the killing, I also have to admire the
technology.
Morris Dovey
West Des Moines, Iowa USA
On the issue of the style guideline quote, it was intended as a mild
joke, as you seem to be a letter-of-the-law type person - at least
more so than me, in the context of programming.
On the issue of NDEBUG, I made a silly mistake again. I would have the
checks enabled on both the version tested, the version released, and
the version debugged - indeed all three would be the same executable.
Thus I think maybe we're arguing for the same thing.
As for how this would affect you're example, well, the 'assert' would
have a different name like 'subsystem_name_assert' or something. The
point is that it is possible to need more testing than your example
implies (in this case a set of new assertion functions and a
supporting infrastructure) - not that the code you presented was wrong
in itself.
You seem to argue repeatedly that program errors should not appear in
released code. I argue that even so, it is going to happen, and there
are contexts where it is important that even the program itself can
detect program faults as soon as possible and attempt to deal with
them. Contexts where this is the case include safety critical systems.
Is this so unreasonable. If so, please direct your arguments to the
British M.O.D. and American D.O.D., to developers for commercial
embedded systems, and anyone else you can think of.
The code for recovery I was talking about was not 'development
scaffolding' - it was a designed in feature of each of the systems
where it was identified as a requirement.
>> Code is written by humans. The peers that review it are humans. The
I believe pilots are unhappy about death because I'm pretty sure that
being suicidal is against the rules for pilots - those aircraft are
pretty expensive ;-)
>I agree. Can't you read? Testing is what you do to production code.
>NDEBUG is not defined during development - it's defined during testing.
>Therefore, what you test is non-assert code, and what you give to the
>users is non-assert code. Assertions are for programmers, not for
>testers.
OK - so you see programmers as doing some sort of pre-testing phase of
testing. Not all programmers have access to the necessary hardware.
After all, I never had an helicopter gunship on my desk - I think I'd
have noticed if I did!
You seem to be using asserts where I would use a more extensive unit
testing approach (at least where given the choice).
I never trust a C implementation to do anything for me ;-)
For the example you're after, a real example would be pretty long,
because it's a large-system problem. The point is that bugs can occur
very rarely, due to needing a combination of state and action. The
problem cases are the ones which don't seem that special until you
find the bug.
but just imagine that a user command is received. You are supposed to
interpret that command and create a structure to hold the details. One
unusual case has a bug where the pointer to the structure is left NULL
- but only in a certain state depending on previous commands.
In a large system, this happens all the time
Unit testing tries to address this by forcing you to consider all
combinations of conditions, but because it takes the functions out of
context, problems can still occur.
>const int a = 5;
>const int b = 6;
>int c = a + b;
>
>if(c != a + b)
>{
LOL - I have NEVER done this, but I've none people who did.
For the record, if you can't trust the '+' operator then you can't
trust anything the compiler generates - including the 'if'. Which I
don't - but this isn't the way to catch out the compiler ;-)
>Are you seriously telling me that you'd leave the assertion in place in
>production code?
<snip example - this is getting too damned long>
I don't think I'd do that in the first place.
>Sure. But there's imperfect and then there's dumb. I'm not saying
>/you're/ dumb - but that code was. I'd better stop going on about it
>before you get thoroughly annoyed by it, but please remember that void
>main is the mark of a newbie in comp.lang.c, okay? (In other words, it's
>the mark of someone who hasn't yet learned that the language is defined
>not by their compiler or by what seems to work, but by the ISO C
>Standard.) People who continue not to understand this graduate from
>newbie to lamer real fast.
How about newbie to multi-lingual and sometimes forgets the odd detail
of one of the languages I'm not using too much ATM.
Will get back to remainder later - about to miss my lift home...
--
Steve Horne
s...@ttsoftware.co.uk
Obviously you have a collection of PERFECT programmers and testers.
Given the choice between destroying the users (often precious) data
and halting with an obscure error message, I will take the message.
If your testing is really perfect, that halt will never occur. If
not, you can explain to the user that that was there to protect
his data - as a lifesaver. In addition you can fix it.
This is the same thing as the old game of defensive programming,
i.e. test for and report the "can't happen" cases.
--
Chuck Falconer (cbfal...@my-deja.com)
http://www.qwikpages.com/backstreets/cbfalconer/
>Steve Horne <s...@ttsoftware.co.uk> wrote:
>
>> Therefore, NDEBUG should never be defined. I thought everyone knew
>> this.
>
>Yes, it should. It's quite simple: if you ever, _ever_, inflict an
>assert failure on one of my users, I will do you physical harm. It is
>simply not something that a user should ever be able to see. An assert
>failure during user run time means that the program wasn't fit for prime
>time yet, and that programmer _and_ quality control failed big time.
If the program is complex enough, no amount of quality control may
guarantee that it is bug free. If the program is bug free, indeed,
leaving the asserts enabled is not going to hurt anybody (unless they are
in some very tight loops, which is normally not the case). If the
program is not bug free, the assert makes the difference between
producing wrong results or mysteriously crashing (i.e. the usual behaviour
of Windows apps :-) and letting the user know that there is a problem
that needs to be reported to the program maintainer.
>If you don't understand the common user well enough to know that assert
>failures are too technical for them to handle, you should be programming
>in VBA, not in C.
It is relatively easy to make your own assert, whose output is
explanatory enough for the user, while still producing the typical assert
information, needed by the maintainer.
>In article <3a0047c8....@news.worldonline.nl>,
> in...@hoekstra-uitgeverij.nl (Richard Bos) wrote:
>> Steve Horne <s...@ttsoftware.co.uk> wrote:
>>
>> > Therefore, NDEBUG should never be defined. I thought everyone
>> > knew this.
>>
>> Yes, it should. It's quite simple: if you ever, _ever_, inflict
>> an assert failure on one of my users, I will do you physical harm.
>> It is simply not something that a user should ever be able to see.
>> An assert failure during user run time means that the program
>> wasn't fit for prime time yet, and that programmer _and_ quality
>> control failed big time.
>
>Obviously you have a collection of PERFECT programmers and testers.
I think you misunderstand Richard's idea. which is (I think) that
assert should only appear in dev code to indicate an "impossible"
event has arisen. You should never use assert in production code to
trap something you could have programmed round eg memory allocation
failure. How can you possibly justify aborting the program for such an
event...
>Given the choice between destroying the users (often precious) data
>and halting with an obscure error message, I will take the message.
You must work for MS, with their lovely "Error 453" messages that VB
produces. If you seriously think that this is a good choice of error
handling, you're out of your mind. You should trap the error properly,
and then save the users data before aborting...
>If your testing is really perfect, that halt will never occur. If
>not, you can explain to the user that that was there to protect
>his data - as a lifesaver. In addition you can fix it.
An assert? Are we talking about the same thing? You seem to be
discussing either C++ exception handling, or else possibly *proper*
error handling?
>This is the same thing as the old game of defensive programming,
>i.e. test for and report the "can't happen" cases.
Agreed.
--
Mark McIntyre
C- FAQ: http://www.eskimo.com/~scs/C-faq/top.html
>On Wed, 01 Nov 2000 21:00:01 GMT, cbfal...@my-deja.com wrote:
>
>>In article <3a0047c8....@news.worldonline.nl>,
>> in...@hoekstra-uitgeverij.nl (Richard Bos) wrote:
>>> Steve Horne <s...@ttsoftware.co.uk> wrote:
>>>
>>> > Therefore, NDEBUG should never be defined. I thought everyone
>>> > knew this.
>>>
>>> Yes, it should. It's quite simple: if you ever, _ever_, inflict
>>> an assert failure on one of my users, I will do you physical harm.
>>> It is simply not something that a user should ever be able to see.
>>> An assert failure during user run time means that the program
>>> wasn't fit for prime time yet, and that programmer _and_ quality
>>> control failed big time.
>>
>>Obviously you have a collection of PERFECT programmers and testers.
>
>I think you misunderstand Richard's idea. which is (I think) that
>assert should only appear in dev code to indicate an "impossible"
>event has arisen.
What happens if an "impossible" event arises in production code? :-)
>You should never use assert in production code to
>trap something you could have programmed round eg memory allocation
>failure.
You should never use it for this purpose in dev code, either. This is
not what assert was invented for. If correctly used, an assert may/should
survive in production code.
>How can you possibly justify aborting the program for such an
>event...
It's easy to justify aborting the program for such an event, but not
with assert. If the program has run out of memory or disk space and
it needs more to continue its execution, there is little else it could
do but stop, with a fully explanatory message.
>What happens if an "impossible" event arises in production code? :-)
This is a good place to introduce one of my favorite quotes on
the subject:
... it is absurd to make elaborate security checks on
debugging runs, when no trust is put in the results, and
then remove them in production runs, when an erroneous
result could be expensive or disasttrous.
C. A. R. Hoare
--
Michael M Rubenstein
The problem with this is that if you want to accept all values between
INT_MIN and INT_MAX, or any particular subset there of that does not
end in a one less than a power of 10, you can't do it for two reasons:
1. You can't portably (at least not easily) do INT_MIN to INT_MAX,
because the number of digits varies with the implementation.
2. You can't do it at all if your range goes close to the limits and
into fractional decimal digits, that is for a 16 bit int with INT_MIN
-32767 or -32768, and INT_MAX 32767, you can't safely use this
technique to accept numbers up to 25000, because you need to accept 5
decimal digits but some combinations of 5 decimal digits overflow.
There are no easy, general purpose solutions to live user input,
because there are far to many variations in live users. But I really
don't like the concept of starting a newbie with scanf(), anymore than
starting with gets(), only to come along later and say, BTW, don't do
that any more, it's lame and buggy.
Jack Klein
--
Home: http://jackklein.home.att.net
Didn't Hoare also put that more succinctly? Namely that removing
self checks in production code is like using life jackets during
training missions and discarding them when actually heading
out to the high seas. Or something like that. :)
--
Any hyperlinks appearing in this article were inserted by the unscrupulous
operators of a Usenet-to-web gateway, without obtaining the proper permission
of the author, who does not endorse any of the linked-to products or services.
> Didn't Hoare also put that more succinctly? Namely that removing
> self checks in production code is like using life jackets during
> training missions and discarding them when actually heading
> out to the high seas. Or something like that. :)
Knuth, in "Structured Programming with go to Statements," said:
[Hoare] points out quite correctly that the current
practice of compiling subscript range checks into the
machine code while a program is being tested, then
suppressing the checks during production runs, is like a
sailor who wears his life preserver while training on
land but leaves it behind when he sails! On the other
hand, that sailor isn't so foolish if life vests are
extremely expensive, and if he is such an excellent
swimmer that the chance of needing one is quite small
compared with the other risks he is taking. In the
foregoing examples we typically are much more certain
that the subscripts will be in range than that other
aspects of our overall program will work correctly.
--
Just another C hacker.
Ah, thank you. I can now happily go back to #define NDEBUG. ;)
>On Wed, 01 Nov 2000 21:54:48 -0500, Michael Rubenstein <mi...@mrubenstein.com>
>wrote:
>>This is a good place to introduce one of my favorite quotes on
>>the subject:
>>
>> ... it is absurd to make elaborate security checks on
>> debugging runs, when no trust is put in the results, and
>> then remove them in production runs, when an erroneous
>> result could be expensive or disasttrous.
>>
>> C. A. R. Hoare
>
>Didn't Hoare also put that more succinctly? Namely that removing
>self checks in production code is like using life jackets during
>training missions and discarding them when actually heading
>out to the high seas. Or something like that. :)
Yes, that immediately follows the passage I quoted:
What would we think of a sailing enthusiast who wears his
life-jacket when training on dry land but takes it off as
soon as he goes to sea?
--
Michael M Rubenstein
>On 1 Nov 2000 15:06:44 GMT, Dan...@cern.ch (Dan Pop) wrote in
>comp.lang.c:
>
>> #include <stdio.h>
>>
>> int main()
>> {
>> int i;
>> char c;
>> fprintf(stderr, "Enter a number between 0 and 9999: ");
>> fflush(stderr);
>> if (scanf("%4d%c", &i, &c) < 2 || c != '\n' || i < 0)
>> printf("You're a bad boy\n");
>> else printf("Good boy, you've entered: %d\n", i);
>> return 0;
>> }
>>
>The problem with this is that if you want to accept all values between
>INT_MIN and INT_MAX, or any particular subset there of that does not
>end in a one less than a power of 10, you can't do it for two reasons:
This is a moot issue as far as the newbie is concerned: he wants to be
able to get some data to test his program without having to recompile
it for each data set. He has no need for covering the full range of an
int, especially since he may not even know what that is, at that point.
>There are no easy, general purpose solutions to live user input,
>because there are far to many variations in live users.
As I pointed out in my previous post (and you apparently didn't read),
these programs have exactly one user, the newbie who wrote them. Newbies
aren't supposed to write production code, are they? :-)
>But I really
>don't like the concept of starting a newbie with scanf(), anymore than
>starting with gets(), only to come along later and say, BTW, don't do
>that any more, it's lame and buggy.
As shown above, it is extremely easy to use scanf in a safe way, provided
you don't attempt to perform any error recovery (you abort the program at
the first conversion error). No other alternative is available to the
newbie, who has yet to learn about such esoteric things as stdin and
pointers. Without scanf, the newbie would be able to get numeric user
input towards the end of the C course (or book). With scanf, this is
available soon after the "hello world" program. The effort in learning it
is not wasted, anyway, because there are plenty of good uses for sscanf.
What makes you think that calling assert() will protect the user's data?
To turn Mr Horne's example on its head, what makes you think the pilot
of a military aircraft will thank you for leaving asserts in place, if
it means that his fly-by-wire stops working at 600mph in mountainous
terrain?
If leaving assert() in place can protect your user's data, you are using
assert() incorrectly.
> This is the same thing as the old game of defensive programming,
> i.e. test for and report the "can't happen" cases.
Sure. Assert that they can't happen, and hammer the program until they
do. Then fix the program and repeat until completely convinced. That's
not testing - that's debugging. Once you've /finished/ debugging, you're
ready to test. At that point, you're not debugging any more, are you? So
turn off debugging by defining NDEBUG. It's really very simple.
I agree that it has gone on for a long time, and that the debate is in
danger of descending into personal attacks.
>
> On the issue of the style guideline quote, it was intended as a mild
> joke, as you seem to be a letter-of-the-law type person - at least
> more so than me, in the context of programming.
I looked for humour (one must, you know) but found none. Perhaps you had
better explain the joke (if it is worth explaining).
>
> On the issue of NDEBUG, I made a silly mistake again. I would have the
> checks enabled on both the version tested, the version released, and
> the version debugged - indeed all three would be the same executable.
>
> Thus I think maybe we're arguing for the same thing.
No, we're not. I would have the checks enabled on the developer version
only. Assertions are for programmers' eyes only, not those of testers or
of users.
>
> As for how this would affect you're example, well, the 'assert' would
> have a different name like 'subsystem_name_assert' or something. The
> point is that it is possible to need more testing than your example
> implies (in this case a set of new assertion functions and a
> supporting infrastructure) - not that the code you presented was wrong
> in itself.
If you choose to have some new subsystem which you choose to call
subsystem_name_assert(), then obviously I have no a priori knowledge of
it, so I can't rationally comment on anything except, perhaps, its name,
which I would consider misleading.
Such extra subsystems are common. If they are part of the production
design, then they remain in the production code, and they should be
tested like any production code.
>
> You seem to argue repeatedly that program errors should not appear in
> released code. I argue that even so, it is going to happen, and there
> are contexts where it is important that even the program itself can
> detect program faults as soon as possible and attempt to deal with
> them. Contexts where this is the case include safety critical systems.
I certainly agree that error-checking is vital and that, for
safety-critical systems, it is especially vital. I disagree that
assert() is the way to achieve this in production code.
<snip>
>
> The code for recovery I was talking about was not 'development
> scaffolding' - it was a designed in feature of each of the systems
> where it was identified as a requirement.
assert() is development scaffolding. You must be talking about something
else.
>
> >> Code is written by humans. The peers that review it are humans. The
> I believe pilots are unhappy about death because I'm pretty sure that
> being suicidal is against the rules for pilots - those aircraft are
> pretty expensive ;-)
>
> >I agree. Can't you read? Testing is what you do to production code.
> >NDEBUG is not defined during development - it's defined during testing.
> >Therefore, what you test is non-assert code, and what you give to the
> >users is non-assert code. Assertions are for programmers, not for
> >testers.
>
> OK - so you see programmers as doing some sort of pre-testing phase of
> testing.
No. I see programmers as doing some sort of pre-testing phase called
"debugging". It's what assert() is for.
Once debugging is over, we define NDEBUG. The N is short for "NO" or
"NOT", you see. It means debugging is over, and it's time to test.
Because we are no longer debugging, we no longer need debugging-only
subsystems such as assert(). Hence, defining NDEBUG (and thus asserting
that we are not debugging) turns off the debugging-only subsystems. How
hard /is/ this to understand?
> Not all programmers have access to the necessary hardware.
> After all, I never had an helicopter gunship on my desk - I think I'd
> have noticed if I did!
Try tidying your desk. :-)
>
> You seem to be using asserts where I would use a more extensive unit
> testing approach (at least where given the choice).
I would use /both/.
>
> I never trust a C implementation to do anything for me ;-)
If the C implementation does not work in accordance with the ISO C
Standard, ditch it and get a working implementation instead. Of course,
the onus is on you to ensure that your code conforms with that same
Standard.
>
> For the example you're after, a real example would be pretty long,
I know. I wasn't expecting you to provide one, and I do not consider
your lack of example to damage your argument, for this precise reason.
> because it's a large-system problem. The point is that bugs can occur
> very rarely, due to needing a combination of state and action. The
> problem cases are the ones which don't seem that special until you
> find the bug.
Yes, I do understand the problem. I, too, have worked on large systems,
astonishing as it may sound.
>
> but just imagine that a user command is received. You are supposed to
> interpret that command and create a structure to hold the details. One
> unusual case has a bug where the pointer to the structure is left NULL
> - but only in a certain state depending on previous commands.
Sounds like very ragged code to me. Why wasn't the code slung out at
peer review?
>
> In a large system, this happens all the time
In some large systems, this might happen all the time. In other large
systems, it doesn't. It's a question of quality.
>
> Unit testing tries to address this by forcing you to consider all
> combinations of conditions, but because it takes the functions out of
> context, problems can still occur.
That's what integration testing is for.
>
> >const int a = 5;
> >const int b = 6;
> >int c = a + b;
> >
> >if(c != a + b)
> >{
>
> LOL - I have NEVER done this, but I've none people who did.
>
> For the record, if you can't trust the '+' operator then you can't
> trust anything the compiler generates - including the 'if'. Which I
> don't - but this isn't the way to catch out the compiler ;-)
I agree. So why would you include the check for something which simply
cannot happen, in production code?
>
> >Are you seriously telling me that you'd leave the assertion in place in
> >production code?
>
> <snip example - this is getting too damned long>
>
> I don't think I'd do that in the first place.
You mean you'd never write a protective firewall of data validation
functions, to ensure that the inner core of functions only ever gets
good data? Why not?
<snip>
>On Wed, 01 Nov 2000 21:00:01 GMT, cbfal...@my-deja.com wrote:
>
>>In article <3a0047c8....@news.worldonline.nl>,
>> in...@hoekstra-uitgeverij.nl (Richard Bos) wrote:
>>> Steve Horne <s...@ttsoftware.co.uk> wrote:
>>>
>>> > Therefore, NDEBUG should never be defined. I thought everyone
>>> > knew this.
>>>
>>> Yes, it should. It's quite simple: if you ever, _ever_, inflict
>>> an assert failure on one of my users, I will do you physical harm.
>>> It is simply not something that a user should ever be able to see.
>>> An assert failure during user run time means that the program
>>> wasn't fit for prime time yet, and that programmer _and_ quality
>>> control failed big time.
>>
>>Obviously you have a collection of PERFECT programmers and testers.
>
>I think you misunderstand Richard's idea. which is (I think) that
>assert should only appear in dev code to indicate an "impossible"
>event has arisen. You should never use assert in production code to
>trap something you could have programmed round eg memory allocation
>failure. How can you possibly justify aborting the program for such an
>event...
I don't - I use a customised assert to attempt to trap inconsistent
situations arising from program errors - and I only do that when
working on safety critical systems where it is identified as a
requirement. The customised assert emphatically does NOT bomb the
program out - it triggers recovery actions, potentially including
deferring to a backup system with (hopefully) different bugs. Also,
because I'm sure someone is about to criticise me for something else,
I'll point out the customised asserts do not have the name 'assert',
because yes I know that that is a reserved name.
All of this has been covered in this thread. The whole point of the
original explanation was the simple fact that milage varies in terms
of the amount of testing required in different circumstances, A BIG
PART OF MY POINT WAS THAT BOMBING OUT IS NOT ACCEPTABLE.
Personally I am getting sick of the constant assumption that anyone
who could possibly make a mistake (yes, I confess, I've made a couple,
being the human that I am) must therefore be an incompetent arsehole.
>An assert? Are we talking about the same thing? You seem to be
>discussing either C++ exception handling, or else possibly *proper*
>error handling?
Oh dear oh dear - we are talking about one of those 'we have our own
meaning for this word, and refuse to accept the possibility of any
pre-existing or wider meaning' narrow-minded viewpoints, aren't we.
If anyone actually cares to read what I have written before
criticising it, please feel free. I suggest e-mail - this group is
getting to be more hassle than it is worth.
--
Steve Horne
s...@ttsoftware.co.uk
Exactly. Where I put asserts in the code, they will stay there
in production - but I expect them to NEVER be triggered. In C
we can be very selective about the checked areas, but in Pascal
it is usually all or nothing. I virtually always leave range
checking enabled in Pascal. Unfortunately C does not have the
concept of a sub-range - enter assert.
I certainly do not recommend using them to signal such things as
out-of-memory failures, bad user input, etc. etc. etc.
Does it abort the program if the customised assertion fails? If so, it
ain't assert(). It's bog-standard error checking.
> to attempt to trap inconsistent
> situations arising from program errors - and I only do that when
> working on safety critical systems where it is identified as a
> requirement. The customised assert emphatically does NOT bomb the
> program out
Then it isn't an assert() in the C sense of the word, and we have been
arguing pointlessly because you have been misusing a term which is
defined by the C Standard.
- it triggers recovery actions, potentially including
> deferring to a backup system with (hopefully) different bugs. Also,
> because I'm sure someone is about to criticise me for something else,
> I'll point out the customised asserts do not have the name 'assert',
> because yes I know that that is a reserved name.
You've been arguing for a long time now that asserts should be left in
code. You are also arguing that the program should not bomb out with an
assertion failure. Using the ISO C assert() statement, these two goals
are incompatible. If you are merely using a few error-checking routines,
well, bully for you - that's pretty much run-of-the-mill stuff.
>
> All of this has been covered in this thread. The whole point of the
> original explanation was the simple fact that milage varies in terms
> of the amount of testing required in different circumstances, A BIG
> PART OF MY POINT WAS THAT BOMBING OUT IS NOT ACCEPTABLE.
Right. And assert() bombs out. So you don't put it in production code.
So you define NDEBUG for testing and production. QED.
>
> Personally I am getting sick of the constant assumption that anyone
> who could possibly make a mistake (yes, I confess, I've made a couple,
> being the human that I am) must therefore be an incompetent arsehole.
I don't see where you're getting that from. The only accusations of
incompetence and inexperience in this thread that are not based purely
on source code have come from you, sir.
>
> >An assert? Are we talking about the same thing? You seem to be
> >discussing either C++ exception handling, or else possibly *proper*
> >error handling?
>
> Oh dear oh dear - we are talking about one of those 'we have our own
> meaning for this word, and refuse to accept the possibility of any
> pre-existing or wider meaning' narrow-minded viewpoints, aren't we.
This is comp.lang.c, where we discuss ISO C (and, for hysterical
raisins, K&R C). For the sake of ***clarity***, we therefore use ISO C's
definitions where the ISO C Standard provides them - to ***ensure***
that we are all talking about the same thing.
Your not-quite-assert error-checking routines are a case in point.
Whenever you said "assert", everyone thought you meant "assert", not "my
own personal version of assert which isn't called assert, doesn't act
like assert, and doesn't even abort the program".
If you're going to play Humpty-Dumpty and make up your own meanings for
words, at least do us the courtesy of stating that you are doing so in
advance.
ISO C defines assert as follows (note, this is the C99 definition, which
differs ever-so-slightly from the C90 definition, which takes an integer
expression rather than a scalar expression - C99 is effectively
codifying assert(p); where p is a pointer value - in C90 this was,
technically, undefined):
7.2.1 Program diagnostics
7.2.1.1 The assert macro
Synopsis
[#1]
#include <assert.h>
void assert(scalar expression); |
Description
[#2] The assert macro puts diagnostic tests into programs; |
it expands to a void expression. When it is executed, if
expression (which shall have a scalar type) is false (that |
is, compares equal to 0), the assert macro writes
information about the particular call that failed (including
the text of the argument, the name of the source file, the
source line number, and the name of the enclosing function
-- the latter are respectively the values of the
preprocessing macros __FILE__ and __LINE__ and of the
identifier __func__) on the standard error file in an
implementation-defined format.148) It then calls the abort
function.
>
> If anyone actually cares to read what I have written before
> criticising it, please feel free. I suggest e-mail - this group is
> getting to be more hassle than it is worth.
I don't think you yet understand that comp.lang.c discusses ISO C, not
"Steve Horne C".
Let us assume the perfect programmer/tester group. Today, 99% of
PCs are delivered with no memory checking, neither ECC nor parity.
In the process of copying an executable it is definitely possible
that alpha particles or cosmic rays drop a bit in the buffer used.
The result is a potential bomb.
Leaving the asserts in offers some measure of protection.
Note that I DO NOT recommend having or buying such crippled memory
systems. But your users will, and have no idea of the potential
foulups. If they ask for ECC memory they may very well be derogated
by their own suppliers and/or service groups. I know I have.
In fact I suspect that a fair proportion of the curses Windows
draws is due to just such a mechanism.
See my previous answer scenario with cosmic rays and no ECC etc.
Your pilot would rather have the system release the controls to
him than dig holes in the ground.
As you may gather, I have fairly strong feelings against turning
off debug assistance.
How the hell do you debug a program that has never been run?
The only possible explanation is that we have too different views of
the development lifecyle. Here is mine (not to be taken as an
absolute, but it is a fair model)...
1. Initial high level requirements and feasability
2. Detailed requirements
3. Design
4. Coding - including peer review and immediate fixes, plus
a degree of initial component testing.
5. Unit testing + debugging, extra review etc where necessary
6. System testing + debugging, extra review + unit testing
where necessary.
7. Acceptance and release
8. Maintenance - including debugging, extra review, extra
testing etc where necessary.
What is yours? How exactly do you debug the bugs that no-one has found
yet?
That 'just imagine' scenario is in the context of a system with
several million lines of code, and surely you can see that the odd
inconsistency can occur between what one programmer thinks he is
supposed to provide and what another thinks he is being provided with,
and occasionally someone might forget to do a check on some data that
he expects to be already set up anyway.
And do you really think there is a completely 100% compliant standard
C compiler anywhere? Compiler writers are human too.
Yes I would do data validation checks - always. I have said so several
times in this thread. As your only interest in this thread is to
distort everything I say, I see no point in continuing.
--
Steve Horne
s...@ttsoftware.co.uk
>To turn Mr Horne's example on its head, what makes you think the pilot
>of a military aircraft will thank you for leaving asserts in place, if
>it means that his fly-by-wire stops working at 600mph in mountainous
>terrain?
You know full well that is not what I suggested.
>If leaving assert() in place can protect your user's data, you are using
>assert() incorrectly.
It can prevent you from writing a new, corrupt file over the top of
the previous valid one, for instance. Sounds like protecting data to
me. Though I still wouldn't let the program bomb.
>
>> This is the same thing as the old game of defensive programming,
>> i.e. test for and report the "can't happen" cases.
>
>Sure. Assert that they can't happen, and hammer the program until they
>do. Then fix the program and repeat until completely convinced. That's
>not testing - that's debugging. Once you've /finished/ debugging, you're
>ready to test. At that point, you're not debugging any more, are you? So
>turn off debugging by defining NDEBUG. It's really very simple.
Rubbish - testing is the process of detecting the presence and
conditions in which bugs occur. Debugging is the process of
identifying the causes of those bugs and fixing them. Clearly what you
are referring to by 'hammering' the program is testing.
--
Steve Horne
s...@ttsoftware.co.uk
Debugging is the art of trying to mend your program when you know it's
broken.
Testing is the the art of trying to break your program when you think it
works.
I thought I'd coined those expressions myself, but I discovered that
Kernighan and Pike say something similar (might even be word for word)
in "The Practice of Programming".
>
> How the hell do you debug a program that has never been run?
Run it.
Code doesn't go to test until it's been peer-reviewed, and there's no
mileage in a formal peer review until the programmer believes the code
to be correct. The good programmer will know that the code is unlikely
to be correct until he's done an appropriate amount of debugging work on
it.
> The only possible explanation is that we have too different views of
> the development lifecyle. Here is mine (not to be taken as an
> absolute, but it is a fair model)...
>
> 1. Initial high level requirements and feasability
>
> 2. Detailed requirements
>
> 3. Design
>
> 4. Coding - including peer review and immediate fixes, plus
> a degree of initial component testing.
>
> 5. Unit testing + debugging, extra review etc where necessary
>
> 6. System testing + debugging, extra review + unit testing
> where necessary.
>
> 7. Acceptance and release
>
> 8. Maintenance - including debugging, extra review, extra
> testing etc where necessary.
>
> What is yours?
Very kind of you; mine's a pint. :-)
What you call "immediate fixes", I call "debugging". Terminology again,
you see.
4. Coding and debugging (the two processes go very much hand in hand)
4a. Peer review.
4b. If need be, loop back to 4.
Debugging is something a programmer does. Testing is something a tester
does. If a tester finds a flaw, the code returns to a programmer for
debugging.
> How exactly do you debug the bugs that no-one has found
> yet?
Find them.
> That 'just imagine' scenario is in the context of a system with
> several million lines of code, and surely you can see that the odd
> inconsistency can occur between what one programmer thinks he is
> supposed to provide and what another thinks he is being provided with,
> and occasionally someone might forget to do a check on some data that
> he expects to be already set up anyway.
And that's what assert() statements are for.
>
> And do you really think there is a completely 100% compliant standard
> C compiler anywhere? Compiler writers are human too.
Right. So, in the extremely rare event that you find what you consider
to be a genuine bug in the compiler, you log it with the vendor. Chances
are high that the vendor will explain to you, with references to the ISO
C Standard, why the compiler is in fact behaving correctly and that your
code is in fact incorrect.
> Yes I would do data validation checks - always. I have said so several
> times in this thread. As your only interest in this thread is to
> distort everything I say, I see no point in continuing.
Your mind-reading equipment is faulty. My interest in this thread is a
discussion of the issues raised in the course of it. Whether you
continue in the discussion is entirely your affair.
Well, I'm certainly confused because I know that no self-respecting
programmer (in which group I would certainly include you) would suggest
such a thing, and yet you apparently suggested leaving assert()s in the
code. It has since become apparent that you have a different meaning of
assert() from the rest of the C programming community, which explains
the dichotomy.
>
> >If leaving assert() in place can protect your user's data, you are using
> >assert() incorrectly.
>
> It can prevent you from writing a new, corrupt file over the top of
> the previous valid one, for instance. Sounds like protecting data to
> me.
Not an appropriate use of assert(). This should be handled by normal
error-checking, not debug-only assertions.
> Though I still wouldn't let the program bomb.
Then you're not using assert(). We've established this elsethread.
You're using something which you call by a similar name, but which has a
different purpose.
> >> This is the same thing as the old game of defensive programming,
> >> i.e. test for and report the "can't happen" cases.
> >
> >Sure. Assert that they can't happen, and hammer the program until they
> >do. Then fix the program and repeat until completely convinced. That's
> >not testing - that's debugging. Once you've /finished/ debugging, you're
> >ready to test. At that point, you're not debugging any more, are you? So
> >turn off debugging by defining NDEBUG. It's really very simple.
>
> Rubbish - testing is the process of detecting the presence and
> conditions in which bugs occur. Debugging is the process of
> identifying the causes of those bugs and fixing them. Clearly what you
> are referring to by 'hammering' the program is testing.
Since neither testing nor debugging is defined by the ISO C Standard,
you can pick your own meanings for them and I cannot topically
contradict you. So I won't.
>Then you're not using assert(). We've established this elsethread.
>You're using something which you call by a similar name, but which has a
>different purpose.
The word 'assert' predates the C standard, and has a common meaning in
programming in general. The existence of a specific interpretation of
this meaning in the standard library does not mean the concept can
never be used in a different way. I have told you on several occasions
that I was using a custom assert function, which has a different name.
As I recall, you took offense at the fact I use a name convention
intended to allow a seperate minor variation of assert in each
subsystem of a program - so you can hardly claim to be unaware of this
fact.
Clearly we are not able to communicate with each other in a positive
useful way. I shall continue to read your replies elsewhere, as you do
have a lot of useful things to say, but I will not respond to any
further distortions of what I post.
--
Steve Horne
s...@ttsoftware.co.uk
> In article <3a0047c8....@news.worldonline.nl>,
> in...@hoekstra-uitgeverij.nl (Richard Bos) wrote:
> > Steve Horne <s...@ttsoftware.co.uk> wrote:
> >
> > > Therefore, NDEBUG should never be defined. I thought everyone
> > > knew this.
> >
> > Yes, it should. It's quite simple: if you ever, _ever_, inflict
> > an assert failure on one of my users, I will do you physical harm.
> > It is simply not something that a user should ever be able to see.
> > An assert failure during user run time means that the program
> > wasn't fit for prime time yet, and that programmer _and_ quality
> > control failed big time.
>
> Obviously you have a collection of PERFECT programmers and testers.
> Given the choice between destroying the users (often precious) data
> and halting with an obscure error message, I will take the message.
Of course. But I didn't say you shouldn't check, I said you shouldn't
use assert() in places where its output can be seen by a user. It's
perfectly possible to check, and to report and exit gracefully, without
assert(). It is also possible to check, report, and exit _with_
assert(), but not gracefully.
> If your testing is really perfect, that halt will never occur. If
> not, you can explain to the user that that was there to protect
> his data - as a lifesaver. In addition you can fix it.
You can if you're there at the very moment the message appears, and the
program doesn't get taken off the screen as soon as it dumps, and your
user doesn't click away the program himself, and, and... otherwise,
you'll find yourself fixing things like "Insertion failed: wibble frotz
something-I-don't-understand error at line 124, oh wait, it could've
been 241 as well."
> This is the same thing as the old game of defensive programming,
> i.e. test for and report the "can't happen" cases.
Yes, test, yes, report, but don't use assert() for this when a user,
rather than a tester or programmer, is at the keyboard.
Richard
> On Wed, 01 Nov 2000 21:54:48 -0500, Michael Rubenstein <mi...@mrubenstein.com>
> wrote:
> >This is a good place to introduce one of my favorite quotes on
> >the subject:
> >
> > ... it is absurd to make elaborate security checks on
> > debugging runs, when no trust is put in the results, and
> > then remove them in production runs, when an erroneous
> > result could be expensive or disasttrous.
> >
> > C. A. R. Hoare
>
> Didn't Hoare also put that more succinctly? Namely that removing
> self checks in production code is like using life jackets during
> training missions and discarding them when actually heading
> out to the high seas. Or something like that. :)
Removing assert() is more like using a life-jacket while learning to
swim, and removing it when you've learned to.
Of course, the ship you're on should still have a life-boat. It should
be better quality than just a life-jacket. Analogously, production code
should still have data (not program) validation. It should be much more
user-readable than assert().
Richard
Ok, but then you're not talking about assert within the meaning of the
act, you're talking about an assertion mechanism. Nothing wrong with the
general principle, it's the crudity of assert() itself that's
user-useless.
Richard
In article <8trfvt$oov$1...@nnrp1.deja.com> <cbfal...@my-deja.com> wrote:
>In article <3A011841...@antlimited.com>,
>See my previous answer scenario with cosmic rays and no ECC etc.
>Your pilot would rather have the system release the controls to
>him than dig holes in the ground.
If it really is "fly by wire", the system *cannot* "release the
controls" to the pilot, because the system *is* the controls.
(Critical safety systems should have lots of hardware redundancy,
of course, including things like ECC. Also, I am rather skeptical
of that "99%" figure you offered else-thread -- certainly all of
the computers I use have parity or ECC protection. (Admittedly
only one of them falls into the "PC" category. Interestingly, the
Ultrasparc boxes have both ECC and parity: ECC on main and cache
memory, parity on internal CPU datapaths.)
(ECC is pretty easy to do these days: put in 9-bit-"byte" DRAMs
and use a 64-bit-wide data path. This gives 72 "DRAM bits" per
64-bit word, which is enough for ECC. I recall that some of the
early Intel Pentium chipsets that were supposed to do ECC were
buggy, but I would hope the modern ones are all fixed by now.)
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA, USA Domain: to...@bsdi.com +1 510 234 3167
http://claw.bsdi.com/torek/ (not always up) I report spam to abuse@.
I don't dispute it.
>, and has a common meaning in
> programming in general.
I don't dispute that either.
> The existence of a specific interpretation of
> this meaning in the standard library does not mean the concept can
> never be used in a different way.
Believe it or not, I don't dispute that either.
> I have told you on several occasions
> that I was using a custom assert function, which has a different name.
Bear in mind that Usenet isn't an instant medium. You have indeed told
me several times that you are using a customised debugging function
which you think of as an assertion(), but these postings don't arrive at
my doorstep the moment you click on "Send". So it takes me a while to
catch up with you.
> As I recall, you took offense at the fact I use a name convention
> intended to allow a seperate minor variation of assert in each
> subsystem of a program - so you can hardly claim to be unaware of this
> fact.
No, I don't recall taking offence. You'd have to work a bit harder than
that to actually offend me.
I was just pointing out the fact, that's all.
I will simply reiterate that Usenet is a batch medium, so it takes time
for stuff to get around the Net, as a consequence of which there are
times when conversations get a little mixed up.
You appear to have read a reply I wrote this morning before reading a
reply I wrote earlier, as a consequence of which you seem to have
concluded that that is the order in which I wrote them. I could be
wrong, but that's my take on the cause of the misunderstanding.
>
> Clearly we are not able to communicate with each other in a positive
> useful way.
I disagree. :-) In fact, I just read your reply on side effects which,
although not 100% correct (as Chris T pointed out) was nevertheless a
lot clearer-minded than my own reply in the same thread, and was
impressed.
> I shall continue to read your replies elsewhere, as you do
> have a lot of useful things to say
That's not so! :-)
>, but I will not respond to any
> further distortions of what I post.
I'm really not trying to distort - only to understand. And now I think I
do understand - we are in fact arguing about terminology and, except
where there is a common referent authority (and, in comp.lang.c, the ISO
C Standard suffices where appropriate), such debates are always futile
(and heated!).
> Of course. But I didn't say you shouldn't check, I said you shouldn't
> use assert() in places where its output can be seen by a user. It's
> perfectly possible to check, and to report and exit gracefully, without
> assert(). It is also possible to check, report, and exit _with_
> assert(), but not gracefully.
The rule should be that any time your program calls assert (FALSE) there
is a bug in your program that has just showed up. If there is anything
that could go wrong without being caused by a bug in your program then you
should test for it, but not using assert. For example, malloc can return
NULL, printing may fail because your printer runs out of paper; these are
not bugs in your program. They must be handled, but not using assert.
> Yes, test, yes, report, but don't use assert() for this when a user,
> rather than a tester or programmer, is at the keyboard.
It is quite difficult to say in general what should happen if your
shipping product runs into a situation where your development version
would have assert'ed. For example, any assert on the Ariane board
computers will the rocket is in flight will probably cause the rocket to
explode, because that is safer than risking it to drop onto some town.
On the other hand, this may not be the suitable thing to do if an assert
() happens in the board computer of on Airbus.
Then you don't use assert. assert is assert is assert. Once you
customise it, you use an assertion system, but I never said anything
against that, only against assert itself.
Note, btw, that usually a customised assertion system traps both
programming errors and unrecoverable input errors such as necessary
config files going AWOL. assert, however, should be used for programming
errors only.
> > All of this has been covered in this thread. The whole point of the
> > original explanation was the simple fact that milage varies in terms
> > of the amount of testing required in different circumstances, A BIG
> > PART OF MY POINT WAS THAT BOMBING OUT IS NOT ACCEPTABLE.
>
> Let us assume the perfect programmer/tester group. Today, 99% of
> PCs are delivered with no memory checking, neither ECC nor parity.
> In the process of copying an executable it is definitely possible
> that alpha particles or cosmic rays drop a bit in the buffer used.
> The result is a potential bomb.
>
> Leaving the asserts in offers some measure of protection.
Nonsense. assert _is_ a bomb. Leaving in a customised assertion system
offers protection against bombing, leaving in assert guarantees a bomb
sooner or later.
Richard
> On Thu, 02 Nov 2000 11:18:11 +0000, Richard Heathfield
> <ric...@antlimited.com> wrote:
>
> >Then you're not using assert(). We've established this elsethread.
> >You're using something which you call by a similar name, but which has a
> >different purpose.
>
> The word 'assert' predates the C standard,
Immaterial. On this group, both assert and NDEBUG mean those words as
defined by the ISO C Standard. If you want to discuss other meanings of
the words, you'll have to do so in comp.programming or suchlike.
What if I were to go about claiming that in C99, a void main is defined,
because you can do this:
int main(void)
{
}
and the word "void", meaning "empty", predates the C99 standard? You'd
laugh at me, and rightly so. Well, then, why should the same not be true
for assert?
Richard
>Bear in mind that Usenet isn't an instant medium. You have indeed told
>me several times that you are using a customised debugging function
>which you think of as an assertion(), but these postings don't arrive at
>my doorstep the moment you click on "Send". So it takes me a while to
>catch up with you.
So now you can reply to messages before you've received them, eh?
Oops - sorry - said I wouldn't do that.
OK - I will accept message-order confusion as the cause of the chaos.
>> Clearly we are not able to communicate with each other in a positive
>> useful way.
>
>I disagree. :-) In fact, I just read your reply on side effects which,
>although not 100% correct (as Chris T pointed out) was nevertheless a
>lot clearer-minded than my own reply in the same thread, and was
>impressed.
Ah-ha - hadn't realised I'd got a chance to start an argument over
there ;-)
>> I shall continue to read your replies elsewhere, as you do
>> have a lot of useful things to say
>
>That's not so! :-)
Is too - or I'm telling my mummy on you, so ner-ner-ne-ner-ner
--
Steve Horne
s...@ttsoftware.co.uk
>Steve Horne <s...@ttsoftware.co.uk> wrote:
>
>> The word 'assert' predates the C standard,
>
>Immaterial. On this group, both assert and NDEBUG mean those words as
>defined by the ISO C Standard. If you want to discuss other meanings of
>the words, you'll have to do so in comp.programming or suchlike.
The C standard quite clearly allows me to create functions to do
whatever I like. And if they implement an assertion scheme, I will
call them asserts.
I'm sure you could happily use scentences such as 'the main use of
this structure' - yet the usage of 'main' is clearly documented in the
standard and this scentence does not fit that standard.
No wonder so many newbies get berated for being off topic when they
ask perfectly simple questions that any sane person would expect in a
group named 'comp.lang.c'. Everyone here seems to be obsessed!
So far, myself and other people have been berated for...
- Excluding the '#include' lines from code snippets where they were
irrelevant to the topic discussed anyway.
- Comparing and contrasting C with other languages.
- Any mention of any variant of C other than standard or K&R.
- Any mention of platform specifics or compiler specifics in a
question about C (I hope the unix, windows, borland and other
groups don't berate newbies for bringing in supposedly off-topic
questions just because they involve C).
- Any mention of algorithm specifics, even though the implementation
language (ie standard C) has an impact on such choices.
- Using any word that happens to be defined (or even used - the word
assert is merely used in the standard, it is the function that is
defined) for a certain context in any other way, even when that
word predates and is more general than C, and despite the fact that
the context in which the word is used is clearly on topic.
- Human error
As some of you seem to be aiming to create the most sterile group in
history, I strongly suggest the addition of a warning to this effect
right at the top of the FAQ, and in all capitals.
--
Steve Horne
s...@ttsoftware.co.uk
Either that, or I was very slow to catch on to your alternative use of
the word 'assert'. So it might have been my fault. Or it might have been
message order. Or it might have been a mixture of the two.
If my slowness of thought was even partially to blame, I offer my
apologies.
I am still a little puzzled by the way this thread has gone, but I have
no particular desire to rake through it all again.
In article <6os20t8odfsltfq6l...@4ax.com>
Steve Horne <s...@ttsoftware.co.uk> writes:
>The C standard quite clearly allows me to create functions to do
>whatever I like. And if they implement an assertion scheme, I will
>call them asserts.
By doing so, you will confuse others. Much of this thread came
about because you *did* confuse others, no?
"It ain't what we don't know that gets us -- it's what we do know
that just ain't so."
To write clearly and concisely, you need to know the target audience.
If you are going to use a term that your readers either might not
know, or might "know" but use to mean something different, you must
define it. In this case, since assert() is an ANSI/ISO standard
function with well-defined meaning and behavior, and since it is
common to write function names without the parentheses, if you say
"I use assert to ...", many of the readers here will think you mean
assert(), from <assert.h>.
>I'm sure you could happily use sentences such as 'the main use of
>this structure' - yet the usage of 'main' is clearly documented in the
>standard and this scentence does not fit that standard.
In this case, the word "main" functions as an adjective, so anyone
reasonably fluent in English will realise that this "main" is not
the other main().
>No wonder so many newbies get berated for being off topic when they
>ask perfectly simple questions that any sane person would expect in a
>group named 'comp.lang.c'. Everyone here seems to be obsessed!
If the obsession is with precision, is that such a bad thing? Asking
fuzzy questions will get you fuzzy answers.
That is your right (as long as you don't call them assert(), of course),
but if you try to communicate with a group of people whose usage of
jargon terms conflicts with your own personal meanings for those terms,
you must expect a degree of confusion.
>
> I'm sure you could happily use scentences such as 'the main use of
> this structure' - yet the usage of 'main' is clearly documented in the
> standard and this scentence does not fit that standard.
Right. Some jargon terms lend themselves more easily to re-use without
confusion, and you have picked an excellent example of that.
>
> No wonder so many newbies get berated for being off topic when they
> ask perfectly simple questions that any sane person would expect in a
> group named 'comp.lang.c'. Everyone here seems to be obsessed!
Well, those who contribute regularly to comp.lang.c do seem to have a
keen interest in the *comp*uter programming *lang*uage called *C*, yes.
The word "obsessed" may or may not apply. I don't know. You'd have to
ask in a psychiatry newsgroup, I guess.
>
> So far, myself and other people have been berated for...
>
> - Excluding the '#include' lines from code snippets where they were
> irrelevant to the topic discussed anyway.
Excluding the #include is a common newbie error. You will find that,
when people realise that a particular poster has a clue, they will not
complain if he omits a header from a snippet such as:
p = malloc(N);
but they /will/ still complain, of course, if he posts a main() function
without the appropriate headers, since inclusion of main() implies that
an entire program is being posted (unless a specific statement to the
contrary is made).
Now, *why* do they complain? Here's why - comp.lang.c is a technical
newsgroup which is very concerned with the issue of correctness with
respect to the C language and, by extension, with the issue of
correctness in general (even though, strictly, extensions are
off-topic). It is generally considered more polite to risk offending
someone by pointing out their error, and thus drawing their attention to
an aspect of their programming which needs to be addressed, than it is
to allow them to go blindly on, making the same mistakes over and over,
simply to avoid the possibility of offending them. After all, we might
one day jump on a plane for which they have written the fly-by-wire
software (no, this is not a dig, it's just a convenient example), and
we'd much rather they'd learned, when given the opportunity, how to
write correct C, even if the process dented their pride a little.
>
> - Comparing and contrasting C with other languages.
Other languages are not topical here - I believe I've raised this point
with you myself. There was recently (may still be) a thread going on in
comp.std.c in which C and C++ are being compared, with a view to
possible changes to the C language in the future. So if you want to
discuss language comparisons, there are places where it can be topically
done. This is not one of those places.
>
> - Any mention of any variant of C other than standard or K&R.
Again, there are already plenty of newsgroups discussing C variants.
There are Borland groups, Microsoft groups, CodeWarrior groups, gcc
groups, and so on. If you want to talk about specific implementations of
C, there is no shortage of fora in which you can do that. But this is
not one of them.
> - Any mention of platform specifics or compiler specifics in a
> question about C (I hope the unix, windows, borland and other
> groups don't berate newbies for bringing in supposedly off-topic
> questions just because they involve C).
Same answer as to your previous point. Nobody's saying these things
can't be discussed. But they are not topical *here*. There are places
where they are topical.
The topicality issues faced by other newsgroups are, of course, a matter
to be discussed in those newsgroups, not here.
> - Any mention of algorithm specifics, even though the implementation
> language (ie standard C) has an impact on such choices.
Where the fact that C is being used has an impact on algorithm choice,
there is an argument for such a thread being topical. Now, I'm having
trouble thinking of a particular example where a specific algorithm
choice is dictated by the implementation language.
>
> - Using any word that happens to be defined (or even used - the word
> assert is merely used in the standard, it is the function that is
> defined)
assert() is required to be a macro.
[#2] The assert macro shall be implemented as a macro, not
as an actual function. If the macro definition is
suppressed in order to access an actual function, the
behavior is undefined.
> for a certain context in any other way, even when that
> word predates and is more general than C, and despite the fact that
> the context in which the word is used is clearly on topic.
If the ISO C Standard defines a term or a keyword or a function name, it
is not unreasonable, in the absence of information to the contrary, to
assume that, when a person uses such a term or keyword or function name,
that he is in fact referring to that term or keyword or function name.
>
> - Human error
Human error is all too topical here. :-)
>
> As some of you seem to be aiming to create the most sterile group in
> history,
Actually, it's a very entertaining, useful, helpful, vibrant and (yes!)
friendly group. But you have to stick around a while to discover that.
:-)
> I strongly suggest the addition of a warning to this effect
> right at the top of the FAQ, and in all capitals.
That's a reasonable suggestion. I don't agree with it, but that doesn't
stop it being a reasonable and topical suggestion. Why not have a chat
with Steve (no, the other one) and see if you can persuade him to take
it up?
Er, make that "macro". :-)
I agree ECC is pretty easy - but the fact is that it is generally
NOT used in the PC world today - go to your local hardware supplier
and ask about ECC memory - you will almost certainly get a DUH, and
if you examine the machines on display I would be very surprised if
you found any with ECC.
Back to the pilot scenario - I should certainly hope that the system
was built with provision for a shell taking out the CPU box(s) - and
thus manual override. If not, I consider the design fatally flawed.
An unexpected software failure is in the same category, just less
physically impressive. There is no such thing as guaranteed
redundancy - just probabilities. Murphy is alive and well.
Again - I am NOT recommending leaving the asserts as a crutch - but
as a lifesaver for things not thought of.
> I agree ECC is pretty easy - but the fact is that it is generally
> NOT used in the PC world today - go to your local hardware supplier
> and ask about ECC memory - you will almost certainly get a DUH, and
> if you examine the machines on display I would be very surprised if
> you found any with ECC.
The 100 MHz SDRAM in my generic PII/233 is allegedly ECC SDRAM.
--
"I should killfile you where you stand, worthless human." --Kaz
I might. If the program is an interface to a database and its gone into
an undefined state. Shutting down may be better protection for the
user's data than continuing. How can we take sensible recovery action
for an "impossible error"? If we'd anticipated the problem there
wouldn't be an assert there.
> To turn Mr Horne's example on its head, what makes you think the pilot
> of a military aircraft will thank you for leaving asserts in place, if
> it means that his fly-by-wire stops working at 600mph in mountainous
> terrain?
I'm glad to say I don't write safety critical stuff. I'm not smart
enough. I have a cetain respect for people who do, and a certain
unease. Are they smart enough?
Yes asserting in a fly-by-wire would not be good.
> If leaving assert() in place can protect your user's data, you are
> using assert() incorrectly.
database. Your program is in an undefined state. Kill it.
> > This is the same thing as the old game of defensive programming,
> > i.e. test for and report the "can't happen" cases.
>
> Sure. Assert that they can't happen, and hammer the program until they
> do. Then fix the program and repeat until completely convinced. That's
> not testing - that's debugging. Once you've /finished/ debugging,
> you're ready to test. At that point, you're not debugging any more,
> are you? So turn off debugging by defining NDEBUG. It's really very
> simple.
If you are completly convinced the can't-happens really can't happen,
then the assert()s are harmless. Leave them alone.
This one *is* religious one and I've argued it before. No real
conclusion then either.
On the other hand a Borland library once asserted on me. No source
code, minimal documentation. What use is a file and line number then? I
re-implemented the library myself (no asserts). I can still remember
how annoyed I was.
--
Dan Pop: "When was the last time you've implemented a real life
application as a strictly conforming program?"
Richard Heathfield: "About 20 minutes ago. It was a new, heavily
optimised pig-launching routine"
I've had PVCS do exactly the same thing. And I was even more annoyed
than you.
No, really I was.
<i've trimmed a bit here, I hope I got attributions right>
> > Obviously you have a collection of PERFECT programmers and testers.
> > Given the choice between destroying the users (often precious) data
> > and halting with an obscure error message, I will take the message.
>
> Of course. But I didn't say you shouldn't check, I said you shouldn't
> use assert() in places where its output can be seen by a user. It's
> perfectly possible to check, and to report and exit gracefully,
> without assert(). It is also possible to check, report, and exit
> _with_ assert(), but not gracefully.
>
> > If your testing is really perfect, that halt will never occur. If
> > not, you can explain to the user that that was there to protect
> > his data - as a lifesaver. In addition you can fix it.
>
> You can if you're there at the very moment the message appears, and
> the program doesn't get taken off the screen as soon as it dumps, and
> your user doesn't click away the program himself, and, and...
> otherwise, you'll find yourself fixing things like "Insertion failed:
> wibble frotz something-I-don't-understand error at line 124, oh wait,
> it could've been 241 as well."
>
> > This is the same thing as the old game of defensive programming,
> > i.e. test for and report the "can't happen" cases.
>
> Yes, test, yes, report, but don't use assert() for this when a user,
> rather than a tester or programmer, is at the keyboard.
ah. I think I'd like to withdraw my previous post :-). I have to
confess I don't have assert()s in my programs. I have have a modified
assert <duck>. It writes to a log file so I can go and look after the
program has aborted. abort() has its uses because it usually generates
a dump. I notice that unlike Richard H you include testers on your list
of people who see asserts. It does speed up the debugging process if
the test people can provide you with a log file documenting what was
going on before the crash.
<i've trimmed a bit here, I hope I got attributions right>
> > Obviously you have a collection of PERFECT programmers and testers.
> > Given the choice between destroying the users (often precious) data
> > and halting with an obscure error message, I will take the message.
>
> Of course. But I didn't say you shouldn't check, I said you shouldn't
> use assert() in places where its output can be seen by a user. It's
> perfectly possible to check, and to report and exit gracefully,
> without assert(). It is also possible to check, report, and exit
> _with_ assert(), but not gracefully.
>
> > If your testing is really perfect, that halt will never occur. If
> > not, you can explain to the user that that was there to protect
> > his data - as a lifesaver. In addition you can fix it.
>
> You can if you're there at the very moment the message appears, and
> the program doesn't get taken off the screen as soon as it dumps, and
> your user doesn't click away the program himself, and, and...
> otherwise, you'll find yourself fixing things like "Insertion failed:
> wibble frotz something-I-don't-understand error at line 124, oh wait,
> it could've been 241 as well."
>
> > This is the same thing as the old game of defensive programming,
> > i.e. test for and report the "can't happen" cases.
>
> Yes, test, yes, report, but don't use assert() for this when a user,
> rather than a tester or programmer, is at the keyboard.
ah. I think I'd like to withdraw my previous post :-). I have to
>Dan...@cern.ch (Dan Pop) wrote:
>
>> In <3a0047c8....@news.worldonline.nl> in...@hoekstra-uitgeverij.nl (Richard Bos) writes:
>>
>> >If you don't understand the common user well enough to know that assert
>> >failures are too technical for them to handle, you should be programming
>> >in VBA, not in C.
>>
>> It is relatively easy to make your own assert, whose output is
>> explanatory enough for the user, while still producing the typical assert
>> information, needed by the maintainer.
>
>Ok, but then you're not talking about assert within the meaning of the
>act, you're talking about an assertion mechanism.
Wrong. My assert is 100% compliant with the definition provided by the
standard. My implementation (for a given particular application) is
better than one provided by the implementor, because its output can be
tuned to the needs of that particular application, while the library one
has to stay generic.
>Nothing wrong with the
>general principle, it's the crudity of assert() itself that's
>user-useless.
If the user is capable of rational thinking, even the implementor assert()
is far from useless: it is *obvious* that something went wrong with the
program and the technical information reported should be passed on to
whomever is responsible for that application. It is most unfortunate that
so many users aren't capable of rational thinking in such circumstances
:-(
Dan
--
Dan Pop
CERN, IT Division
Email: Dan...@cern.ch
Mail: CERN - IT, Bat. 31 1-014, CH-1211 Geneve 23, Switzerland
<snip>
> > Yes, test, yes, report, but don't use assert() for this when a user,
> > rather than a tester or programmer, is at the keyboard.
>
> ah. I think I'd like to withdraw my previous post :-). I have to
> confess I don't have assert()s in my programs. I have have a modified
> assert <duck>. It writes to a log file so I can go and look after the
> program has aborted. abort() has its uses because it usually generates
> a dump. I notice that unlike Richard H you include testers on your list
> of people who see asserts. It does speed up the debugging process if
> the test people can provide you with a log file documenting what was
> going on before the crash.
If you want a log file for use in testing, by all means include a
logfile() function in your program. Call it as often as you like. Flush
it as often as you like. Just don't pretend it's an assert() call, if it
isn't one.
<a long rant in favour of leaving assert()s in production code>
Having read a post by Richard B I'd like to modify my position slightly
(ie. completly recant).
I don't use assert() I use a modified assert.
It writes to a log file so I can examine the assert message after a
crash.
It calls abort() (no it isn't fly by wire) which handily produces a core
dump.
I'll still defend the immediate termination of the program. It's in
an unknown state. It is running wild. Shut it down as quickly as
possible before it does more damage.