reverse a string

J

James Kuyper

Hallvard said:
What? My assumption is that one alternative is to not crash, but
instead let worse damage happen.

What you mean by "let worse damage happen" is what I mean by "crash". I
refer to what assert() does as aborting, not crashing. It's just a
difference in terminology.

....
Yes, code should of course test for bugs in itself and nearby code, and
try to deal gracefully with them. And asserts should of course be
limited to tests where you could be bothered to write the test but not
with coming up with and coding a graceful exit strategy, or where a
"graceful exit strategy" has no or negative value since what you want is
a crash during testing.

Even so, real life doesn't measure up to your ideal of production code,
or any reasonable person's ideal for that matter.

Large portions of real life pay no attention to my ideals, and I don't
have the power to change that. However, it is entirely in my powers to
ensure that the production code I deliver lives up to the ideal
described above, and I have in fact done so, when I could. A large
portion of the code I'm currently responsible for is code that I neither
designed nor wrote - the "real life" you refer to has prevented me from
re-writing all of that code. However, all of my new code, and all of my
modifications to existing code are consistent with that ideal. If that
ideal seems unachievable, then you may have understood it in a more
extreme fashion than I intended when I described it.
 
U

user923005

On Sep 29, 1:43 am, Nick Keighley <[email protected]>
wrote:
[snip]
assert() is of no use to people, such as yourself, who never
make mistakes.

In my experience, assert() comes into its own when working on large
teams.

I know the toolset I have created intimately, as do those who have
created theirs.

But when someone makes an errant use of someone else's code, that is
where it becomes especially valuable.

The assert() is saying "I know this has to be true!"

If anyone violates the assumption, it is easy to find upon debugging.

In release mode, they become NO-OPS, so they cost nothing.
In debug mode, they only fire when something terrible happens that
must be captured.
A waste of time?
Not for me.
 
U

user923005

No. I'm saying that what it actually does (on discovering some
undefined behaviour that it can't deal with gracefully) is up to the
implementation. What it /should/ do is a matter of opinion, which is
/why/ it's up to the implementation.

In my opinion, assert() has no value whatsovever in changing the
behavior of a program and has no relation to its purpose.

The sole purpose and total value of assert() is to find broken logical
assumptions or incorrect use of interfaces when debugging on large
projects.

IMO-YMMV
 
P

Phil Carmody

Richard Heathfield said:
Inadequate testing on target platform. Assertions are not a substitute
for testing.

Indeed. In fact I'd say that asserts are _part_ of testing.

Phil
 
P

Phil Carmody

Richard Heathfield said:
A good programmer will use assertions liberally (where appropriate,
obviously). A good white-box tester will try to fire every assertion.
A good black-box tester can do this with his eyes closed.

I'd take that as a two complements coming from you, except that
the reality is that I'm not a 'tester', I'm simply often a victim
of releases of exceptionally poor quality code.

Oh - how's this for "disabling" asserts in production code - the
production version prints the 'OMG - I shat me pants!' message,
and then doesn't halt, it just blithely continues unirregardless.

Phil
 
N

Nick Keighley

If, of course, they have an alternative which suits them better.

Ah, yes I nearly responded to Richard's apparent contention that we
*can*
write completely correct programs. "If you can't guarantee the
assertion won't fire then write error handling code".

I use a text editor that appears to "recover" from null pointer
dereferences. A dialog comes up with something like "Bad memory
access
at location 0x00000004" and then it continues. Can't you just *feel*
the ice crystals forming in your blood stream? Even the nasal daemons
are cowering.
That claim made such a perfect hook for a small rant on absolute
statements,

Never make an absolute statement someone will always dispute it.
so let me turn it around: "If you're not confident
enough of the code to release it WITH assertions in place, it is
not yet ready for release."
:)

I think each statement makes equally little sense by itself.  Real
life code has bugs, and we need some policy for dealing with them.

If so I'd really, really have liked some
module - any module - to assert() before <bad thing>

OTOH if the system was really buggy, such asserts would crash it
all the time.  

These things require judgement. What Stroustrup called "taste"

Getting back to fopen(), though - it notices it has entered
undefined behavior, maybe due to an argument, maybe an internal
problem.  Should it crash or just return failure?  How serious
harm is the error a symptom of?  If it returns failure and/or logs
a message to stderr, will the user even know or is it just some
daemon which will log a message in some obscure file?  If the user
sees the message, will he just try again to open the file, maybe
succeeding this time?  As the vendor providing that fopen(), how
do you decide?

Turning asserts on or off make most sense depend among other
things on policy, how actively the code it is supported, the
quality of the code, the quality of third-party code which calls
your code, your guess about the harm a bug can do, the
harm/annoyance a crash can do, and thus how much you know of what
your code is used _for_.  Without any of that, ask a soothsayer.

perhaps we need something more sophisticated than on/off

Sometimes clever error recovery conceals errors.

I remember the guy that ignored the "database has encountered a
deadlock"
error. "What's all this in the error log? How come no one complained?
Oh"
 
N

Nick Keighley

OK, quality is getting more attention again, but do try
to paste a common "NO WARRANTY...NO LIABILITY..." notice on a house or a
food product and sell it.

In some countries that is exactly how houses are sold. Caveat Emptor.
 
N

Nick Keighley

On Sep 29, 1:43 am, Nick Keighley <[email protected]>
wrote:

perhaps I should have included a said:
In my experience, assert() comes into its own when working on large
teams.

I still find it handy on one-man projects. Just writing the
assertion helps clarify what a function is and isn't supposed to do.
I end up with a lot more pre-conditions than post-conditions.
I know the toolset I have created intimately, as do those who have
created theirs.

your memory must be better than mine. I knew it intimately when I
wrote it. But now, much later, I'm going to use it.

But when someone makes an errant use of someone else's code, that is
where it becomes especially valuable.

The assert() is saying "I know this has to be true!"

If anyone violates the assumption, it is easy to find upon debugging.

In release mode, they become NO-OPS, so they cost nothing.
In debug mode, they only fire when something terrible happens that
must be captured.
A waste of time?
Not for me.

almost Haiku..
 
T

Tim Rentsch

Richard Heathfield said:
[asking for an example with assert()'s]

Well, I don't think it's a good example. Here's a better one (I
hope!). Consider a function whose documentation says something like:

/* function: si_digital_root
library: libstrint.a
purpose: returns the digital root of a specified number of
digits in a given string
prototype: int si_digital_root(const char *s,
size_t start,
size_t end)
constraints: (1) s shall be non-NULL and at least end characters
in length;
(2) start shall not be greater than end;
(3) all the characters in the specified range
shall be digit characters ('0' to '9').
returns: the digital root of the specified characters.
*/

Here, we have a contract between this function and its caller. This is
a programmatical contract. That is, if the constraints are violated,
it is the fault of the calling function. (The contract makes it clear
that even if the data comes from a user, it is still the calling
function's job to ensure that these constraints are observed.
Therefore, if they are not observed, it indicates a bug in the
program.)

Here is how such a function might use assert() to assist the calling
function's programmer in finding bugs:

#include <string.h> /* for size_t and, in debug mode, strlen */
#ifndef NDEBUG
#include <ctype.h>
#endif

int si_digital_root(const char *s, size_t start, size_t end)
{
int root = 0;
assert(start <= end); /* A */
assert(s != NULL); /* B */
assert(strlen(s) >= end); /* C */
while(start <= end)
{
assert(isdigit(s[start])); /* D */
root += s[start] - '0';
root %= 10;
}

assert(root >= 0 && root <= 9); /* E */
return root;
}

A and B are simple and cheap checks that the constraints have been
observed. C is a slightly more expensive check, since it's O(n).

Not quite. If we take n to be (end-start)+1, or even just end+1,
check C is O( infinity ).

It also involves undefined behavior if the array pointed at
by s is not null terminated.

That's a fair reason for wanting to compile it out of production
code, [snip]

Seems more appropriate to take it as a fair reason to re-write
the assertion C.
 
T

Tim Rentsch

Nick Keighley said:
I figured you deserved a counter analogy. It was actually
a quote by A Famous Computer Scientist (Dijkstra? Hoare? Wirth?)

Tony Hoare, I'm pretty sure.
 
T

Tim Rentsch

Richard Heathfield said:
int si_digital_root(const char *s, size_t start, size_t end)
{
int root = 0;
assert(start <= end); /* A */
assert(s != NULL); /* B */
assert(strlen(s) >= end); /* C */
while(start <= end)
{
assert(isdigit(s[start])); /* D */
root += s[start] - '0';
root %= 10;
}

assert(root >= 0 && root <= 9); /* E */
return root;
}

A and B are simple and cheap checks that the constraints have been
observed. C is a slightly more expensive check, since it's O(n).

Not quite. If we take n to be (end-start)+1, or even just end+1,
check C is O( infinity ).

It also involves undefined behavior if the array [starting at *s]
is not null terminated.

True enough. Can you think of a way to test this constraint (which I
didn't spell out explicitly, but which is implied by the use of
"string" in the docs.
That's a fair reason for wanting to compile it out of production
code, [snip]

Seems more appropriate to take it as a fair reason to re-write
the assertion C.

How would you rewrite it to include a test for s's null-termination,
without yourself constructing a pointer that invokes undefined
behaviour?

If I were writing an assert() for the condition that I think
you want to test, I expect I'd write it as

assert( memchar( s, 0, end ) == 0 );

or perhaps as

assert( memchar( s, 0, start ) == 0 );

and rely on the digits check assertion to test the
characters between 'start' and 'end'.

More likely, though, I wouldn't write the test at all,
since the digits are checked in the loop, and there
doesn't seem to be much point in excluding arguments
that happen to have a zero in the first [0..start)
positions of the *s array. Or, perhaps I would
write an additional function

int
si_digital_root_safe( const char *s, size_t start, size_t end ){
assert( memchar( s, 0, end ) == 0 );
return si_digital_root( s, start, end );
}

and give client code a choice of which function to call.
Having the choice available reduces the impetus for
wanting to remove this assertion for production code.
 
K

Keith Thompson

Tim Rentsch said:
If I were writing an assert() for the condition that I think
you want to test, I expect I'd write it as

assert( memchar( s, 0, end ) == 0 );

or perhaps as

assert( memchar( s, 0, start ) == 0 );

and rely on the digits check assertion to test the
characters between 'start' and 'end'.
[...]

Typo: It's memchr, not memchar.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
If I were writing an assert() for the condition that I think
you want to test, I expect I'd write it as

assert( memchar( s, 0, end ) == 0 );

or perhaps as

assert( memchar( s, 0, start ) == 0 );

and rely on the digits check assertion to test the
characters between 'start' and 'end'.
[...]

Typo: It's memchr, not memchar.

Quite right, thank you for the correction.
 
D

David Thompson

More exactly, assert() evaluates to a null statement if NDEBUG
is defined.
Even more exactly, a void expression with no side effects. It would
have made (as much) sense for assert to be statement-like, but C89
made it nearly (and C99 fully) void-function-like, based I believe on
preexisting practice; the Rationale (I have) doesn't say.
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,199
Latest member
AnyaFlynn6

Latest Threads

Top