a little mistake

B

Barry Schwarz

You have to decide which part of your code is responsible for checking
for "errors". I myself thrive on writing efficient code (I find it
exciting and enjoyable), so if I were to write a function to convert a
decimal digit to an actual integer, I would write:
snip
I would "delegate the responsibility" to the caller function to make sure
that everything is in order. Nonetheless, asserts are free, so the
finished product might be something like:

inline unsigned DigitToInt( int const c )
{
assert( c >= '0' && c <= '9' );


return c - '0';
}

So my program incorporates your function and at some point calls it
with a bad value. Instead of letting me decide how to handle the
error, you have decided that the only possible course of action is to
abort the program.

No thank you. I choose to not give you that much authority over my
code.
When I'm writing functions which deal with strings, I never check for
null pointers -- I delegate that responsibility to the calling function.


Remove del for email
 
F

Frederick Gotham

Barry Schwarz posted:

So my program incorporates your function and at some point calls it
with a bad value.


I will express to you in plain English that calling the function with a
"bad value" will result in Undefined Behaviour, just like the following
will result in undefined behaviour:

#include <stdlib.h>

int main(void)
{
int i;

free (&i);
}

Instead of letting me decide how to handle the
error, you have decided that the only possible course of action is to
abort the program.


No, I have written an assert to facilitate the debugging process.
 
C

CBFalconer

Frederick said:
Tom St Denis posted:
.... snip ...


I'm not sure if the same applies for C, but in C++, the decimal
digit characters are guaranteed to be sequential and in ascending
order.

Can someone please clarify if this holds true for C?

It is, as it is also for Pascal. Your function is easily handled
by a simpler operation:

unsigned dig, value, ch;
....
if ((dig = (ch - '0')) > 9) notadigit();
else {
/* use the conversion */
}

Note that this requires unsigned values. Another use might be:

value = 0;
if (isdigit(ch))
while ((value = (ch - '0')) <= 9) {
number = 10 * number + value;
ch = getchar();
}
/* ch holds char that terminated numerical field */

These sort of things are useful for interactive operation.
 
K

Keith Thompson

Frederick Gotham said:
Tom St Denis posted:
Frederick Gotham wrote: [snip]
inline unsigned DigitToInt( int const c )
{
assert( c >= '0' && c <= '9' );


return c - '0';
}

Not only is this not portable but in 2006 the use of "inline" is
redundant for functions like this. The compiler is smart enough to
figure it out.


I'm not sure if the same applies for C, but in C++, the decimal digit
characters are guaranteed to be sequential and in ascending order.

Can someone please clarify if this holds true for C?

Yes, it's true for C. I think Tom was referring to the "inline"
keyword, which is standard in C99 but not in C90.
 
L

lovecreatesbeauty

Tom St Denis wrote:
What you call efficient I just call lazy.

And you'll never write robust code. Trust me, people will pass stupid
things to your functions.

Sure you can't catch all errors but simple checks for NULL or invalid
symbols is a smart thing to do. It also makes debugging simpler when
you trap errors.

people who can't get functions to work don't read the manual. A good
portion of the others are weak C programmers and don't understand
pointers and the like fully. A simple != NULL or other check may seem
to kill performance [hint: they don't] but are totally worth it.

Thanks. I admire your knowledge and attitude on coding C programs. I
think if programmers are lazy in composing functions, programmers will
be lazier in calling functions.

lovecreatesbeauty
 
P

Peter Nilsson

lovecreatesbeauty said:
The string literal "" just includes a delimiter of null terminator, its
string length is 0.

Let's put the context back in. You originally wrote...
/* Removes extra spaces in string pointed by src, the result string is
pointed by dst. Return value is the length os the result string or
0 for failure. example: "test string" will be "test string" */

You've said that 0 is failure. So, your function 'fails' for an empty
string.

Do you really want the caller to have to check the original string to
differentiate a null pointer failure from an empty string?
int spacerm(char *dst, char *src)
{
said:
if (src == 0 || dst == 0)
Why bother? [Rehtorical] [er... Rhetorical]

If both src and dst are pointers have null values and caller pass these
null pointers into the function, then code inside the function like
this: *dst++ = *src++; will cause undefined behavior. Doing nothing is
better than causing an undefined behavior in my code, I feel.

Even if the pointers are not null, they can still be invalid, so you
still have the
potential to invoke undefined behaviour. There is _no_ portable test
for
valid input in that case!

As the subsequent thread shows, its a debatable issue, but I'd prefer
to use assert() if I had to use anything. At least that can be removed
from the final release.
...
(Joe Estock's code is better than mine, I think it should be an
expert's piece.)

The code that I can see from Joe potentially terminates the string with
a double null byte.

I think most people overcomplicate things. If you just want to
replace two or more spaces with one space, then the following
would do me...

void foo(char *d, const char *s)
{
while (*s)
if ((*d++ = *s++) == ' ')
while (*s == ' ')
s++;
*d = 0;
}
 
L

lovecreatesbeauty

Peter said:
I think most people overcomplicate things. If you just want to
replace two or more spaces with one space, then the following
would do me...

void foo(char *d, const char *s)
{
while (*s)
if ((*d++ = *s++) == ' ')
while (*s == ' ')
s++;
*d = 0;
}

Thank you the great code you shared! I'm pursuing clear logic, compact
and efficient code like yours. Please be lenience and be my mentor not
just nagging.

lovecreatesbeauty
 
T

Tom St Denis

Frederick said:
Think clearly. If I was really that lazy I wouldn't be on this newsgroup
writing code which provides me with no monetary gain.

I don't pretend to know your motives for posting. I'm just speaking
from experience as a designer and implementor.
Then "people" may expect undefined behaviour. I don't write functions to
be used by three year olds.

Yeah, we'd all want smart users of our code. We don't always get that.
And if we dismissed them as stupid you'd find that there are few
people using it. Even something as trivial as say zlib. Still gets
people asking "how do I make a gzip file" questions.

Not only that, but bugs do occur, robust code helps not only curb bugs
by not proceeding on errant paths but detect and report them to the
developer.

I always check the return of mallocs but I've still managed to slip an
unitialized pointer [well I calloc structures] to a function from time
to time. A simple check for NULL picks that up and I'm off to the
races.
I'm not sure if the same applies for C, but in C++, the decimal digit
characters are guaranteed to be sequential and in ascending order.

Can someone please clarify if this holds true for C?

Maybe that's true. I'd use a table anyways. in my ASN.1 IA5 and
Printable routines I have a table of chars and ints. That way
regardless of the mapping I can move from one to another easily and
portably.

Also the use of "inline" for that function is redundant. Smart
compilers will do it anyways and it isn't portable across all compilers
[yes, supporting non-standard compilers like MSVC and borland are often
desired features.]. Except for things like inlined ASM functions the
"inline" keyword is not terribly useful.
Or, wherever possible, I write my code so that it simply can't be
erroneous, for example:


int main(void)
{
int i = 54;

int j = i % 3;

FunctionThatWantsNumberBetweenZeroAndTwoInclusive( j );
}

I'm not saying all your functions have to be param checking. Just the
user exposed ones. Though as your code base increases in complexity
the checking becomes relevant again.
From my experience from my projects and the support email I get, most
people who can't get functions to work don't read the manual. A good
portion of the others are weak C programmers and don't understand
pointers and the like fully. A simple != NULL or other check may seem
to kill performance [hint: they don't] but are totally worth it.

Yes, in the calling function:

Calling function would be part of the users code. I thought I made it
clear there are a lot of weak developers?

Idealism is cool and all, that is, afterall, why I distribute my
projects [http://libtomcrypt.com]. But if you want people to use your
code you have to deal with really shotty programmers.

Tom
 
R

Richard Heathfield

Tom St Denis said:

Not only that, but bugs do occur, robust code helps not only curb bugs
by not proceeding on errant paths but detect and report them to the
developer.

I always check the return of mallocs but I've still managed to slip an
unitialized pointer [well I calloc structures] to a function from time
to time. A simple check for NULL picks that up

No, it doesn't. If the pointer is uninitialised, its value is indeterminate.
Even if it is all-bits-zero (perhaps because it's part of a structure whose
storage was allocated via calloc), that is no guarantee that its value is
NULL.
 
T

Tom St Denis

Richard said:
Tom St Denis said:

Not only that, but bugs do occur, robust code helps not only curb bugs
by not proceeding on errant paths but detect and report them to the
developer.

I always check the return of mallocs but I've still managed to slip an
unitialized pointer [well I calloc structures] to a function from time
to time. A simple check for NULL picks that up

No, it doesn't. If the pointer is uninitialised, its value is indeterminate.
Even if it is all-bits-zero (perhaps because it's part of a structure whose
storage was allocated via calloc), that is no guarantee that its value is
NULL.

Thank you mr. pedantic. Except that on my platforms at least NULL is
all zero.

I don't check for NULL in case you calloc'ed a structure. I check for
NULL in case you pass the return of a heap operation without checking.
If malloc or calloc fails it will be NULL.

The fact that on basically every platform 0 == NULL is just an added
bonus.

Tom
 
R

Richard Bos

Tom St Denis said:
Richard said:
Tom St Denis said:
Not only that, but bugs do occur, robust code helps not only curb bugs
by not proceeding on errant paths but detect and report them to the
developer.

I always check the return of mallocs but I've still managed to slip an
unitialized pointer [well I calloc structures] to a function from time
to time. A simple check for NULL picks that up

No, it doesn't. If the pointer is uninitialised, its value is indeterminate.
Even if it is all-bits-zero (perhaps because it's part of a structure whose
storage was allocated via calloc), that is no guarantee that its value is
NULL.

Thank you mr. pedantic. Except that on my platforms at least NULL is
all zero.

For the time being.
I don't check for NULL in case you calloc'ed a structure. I check for
NULL in case you pass the return of a heap operation without checking.

In some cases, this is a wise precaution; on others, it is superfluous
inefficiency.
Truly uninitialised (or badly arithmeticked) pointers, however, are
allowed to be neither all-bits-zero, nor null. They are allowed to be
trap values, causing your comparison to crash with a segfault or
something similar; they are also allowed to accidentally be equal to a
valid address, causing the comparison to overlook the error and the rest
of the code to write to a completely unrelated area of memory; or
whatever other option the system can think of to embarrass you in front
of your supervisor. Checking for null pointers may catch _some_ of your
errors, but it does not help in the general case.
The fact that on basically every platform 0 == NULL is just an added
bonus.

On _all_ platforms, 0 == NULL. But not on all platforms, a null pointer
is all bits zero. And on some platforms, pointers are larger or smaller
than your prefered integer type; beware printf() errors.

Richard
 
R

Richard Heathfield

Tom St Denis said:
Richard said:
Tom St Denis said:

Not only that, but bugs do occur, robust code helps not only curb bugs
by not proceeding on errant paths but detect and report them to the
developer.

I always check the return of mallocs but I've still managed to slip an
unitialized pointer [well I calloc structures] to a function from time
to time. A simple check for NULL picks that up

No, it doesn't. If the pointer is uninitialised, its value is
indeterminate. Even if it is all-bits-zero (perhaps because it's part of
a structure whose storage was allocated via calloc), that is no guarantee
that its value is NULL.

Thank you mr. pedantic.

Firstly, that isn't my name. Secondly, even if it were, it would require
upper case 'M' and 'P'. Thirdly, please understand that pedantry is a sine
qua non of good programming practice. If you don't like being corrected in
public, don't make mistakes in public. If you don't like my tone, try
softening your own tone towards newbies, and I'll gladly soften my tone
towards you.

Except that on my platforms at least NULL is all zero.

Great - all the world's a Tom St Denis platform, and everything in the
garden is rosy... until it isn't.
I don't check for NULL in case you calloc'ed a structure.

You misunderstood. I was making the point that you claimed that checking for
NULL made it possible to "pick up" uninitialised pointers, which is utter
nonsense.
I check for
NULL in case you pass the return of a heap operation without checking.

That's not what you said earlier. You said you used it to pick up on
uninitialised pointers.
If malloc or calloc fails it will be NULL.
Indeed.

The fact that on basically every platform 0 == NULL is just an added
bonus.

On /all/ platforms, 0 and NULL will compare equal, but that doesn't mean
all-bits-zero means NULL.

if(0 == NULL)
{
puts("I will always be printed.");
}
memcpy(&p, 0, sizeof p);
if(p == NULL)
{
puts("I will probably be printed, but I might not.");
puts("Are you feeling lucky?");
}
 
F

Frederick Gotham

Richard Bos posted:

Truly uninitialised (or badly arithmeticked) pointers, however, are
allowed to be neither all-bits-zero, nor null.


Where are you getting that from?



You have no guarantee whatsoever that the following code won't print all
zeros:


#include <stdlib.h>

int main(void)
{

unsigned counter = 0;

do {


int *guinea_pig_pointer;


const unsigned char *p = (const unsigned char*)
&guinea_pig_pointer;


const unsigned char * const p_over =
(const unsigned char*)(&guinea_pig_pointer + 1);


do
{

printf( "%u", (unsigned)*p++ );

} while ( p != p_over );


printf( "\n" );

} while ( ++counter != 5 );


system("PAUSE");
}
 
C

CBFalconer

Tom said:
.... snip ...

The fact that on basically every platform 0 == NULL is just an
added bonus.

Making that assumption, in any case where it may matter, is
extremely sloppy programming.
 
R

Richard Bos

Frederick Gotham said:
Richard Bos posted:


Where are you getting that from?

The Standard. Uninitialised is uninitialised is not guaranteed any value
_or_ non-value.
You have no guarantee whatsoever that the following code won't print all
zeros:

That's what I wrote. Allowed - not required - to be neither. I didn't
write "not allowed to be either".

Richard
 
T

Tom St Denis

Richard said:
Great - all the world's a Tom St Denis platform, and everything in the
garden is rosy... until it isn't.

I'd actually like you to point out the market share of platforms where

(void*)0

Isn't for all intents and purposes equal to NULL. And more
importantly,

memset(&mypointer, 0, sizeof(void*))

Doesn't produce NULL in mypointer. I know it's not STANDARD. It's
just REALLY REALLY REALLY common. It isn't the reason I check for NULL
though.
You misunderstood. I was making the point that you claimed that checking for
NULL made it possible to "pick up" uninitialised pointers, which is utter
nonsense.

When you calloc a structure with pointers, they're VERY LIKELY to be
equal to NULL. this is the "added bonus" I was talking about. As in,
in addition to the original purpose of the check.
That's not what you said earlier. You said you used it to pick up on
uninitialised pointers.

I said "I've still managed to slip an unitialized pointer [well I
calloc structures]".

I never said it was standard, I just said it was good practice. Again,
because on essentially every platform out there all-zero == NULL.
On /all/ platforms, 0 and NULL will compare equal, but that doesn't mean
all-bits-zero means NULL.

if(0 == NULL)
{
puts("I will always be printed.");
}
memcpy(&p, 0, sizeof p);
if(p == NULL)
{
puts("I will probably be printed, but I might not.");
puts("Are you feeling lucky?");
}

Find me platforms today where that isn't true.

On the one hand you can do a NULL check and trap errors on 99.999% of
platforms in the market. On the other hand you can ignore the check as
frivolous and possibly have unitialized pointers.

I'd recommend to anyone who callocs structures to do a NULL check in
functions. Not because it's standard compliant but just a good
practice and even on not-all-zero NULL platforms the code will still
work [just fail to detect NULL].

Tom
 
A

Andrew Poelstra

I'd recommend to anyone who callocs structures to do a NULL check in
functions. Not because it's standard compliant but just a good
practice and even on not-all-zero NULL platforms the code will still
work [just fail to detect NULL].

Why not make a create_null_structure function to replace calloc for your
purposes? In that function you could also set ints to -1 and then you
could see if a function failed to change other data.

m = create_null_structure(1);

function_on_m (m);

if (m.integer == -1 || m.pointer == NULL)
return FUNCTION_FALIED;

Can't do that with calloc.
 
T

Tom St Denis

Andrew said:
I'd recommend to anyone who callocs structures to do a NULL check in
functions. Not because it's standard compliant but just a good
practice and even on not-all-zero NULL platforms the code will still
work [just fail to detect NULL].

Why not make a create_null_structure function to replace calloc for your
purposes? In that function you could also set ints to -1 and then you
could see if a function failed to change other data.

Not a bad idea. Almost constructive even. Thanks!

:)

Might use that actually.

Look, I wasn't saying it was portable, I just was trying to say that
checking for NULL is a good idea and that on many platforms all-zero ==
NULL is a bonus. You guys read too much into what people say. I'd
gladly trade security for essentially 100% of all applications you'll
touch in your lifetime for a minor portability glitch.

Tom
 
R

Richard Heathfield

Andrew Poelstra said:
I'd recommend to anyone who callocs structures to do a NULL check in
functions. Not because it's standard compliant but just a good
practice and even on not-all-zero NULL platforms the code will still
work [just fail to detect NULL].

Why not make a create_null_structure function to replace calloc for your
purposes?

Because the behaviour of the calloc function is only guaranteed to produce 0
values for integer types, it's not as useful as it might be, so the idea of
replacing calloc is a good one.

The following technique has served me well when creating aggregate objects
of type T:

#include <stdlib.h>
#include "t.h"

T *T_create(int this, double that, long theother)
{
const T blank = {0};

T *new = malloc(sizeof *new);
if(new != NULL)
{
*new = blank; /* get default static initialisers in place */
new->this = this;
new->that = that;
new->theother = theother; /* override specific fields */

/* all other fields in 'new' have their default values */
}
return new;
}

This way guarantees that *either* the allocation fails (in which case 'new'
is NULL) *or* it succeeds and no member object of 'new' will be
uninitialised.
 
K

Kenny McCormack

Tom St Denis said:
Look, I wasn't saying it was portable, I just was trying to say that
checking for NULL is a good idea and that on many platforms all-zero ==
NULL is a bonus. You guys read too much into what people say. I'd
gladly trade security for essentially 100% of all applications you'll
touch in your lifetime for a minor portability glitch.

This is an argument you can't possibly win here. It's too sensible.

See, the problem is, you've positioned yourself as a reasonable person,
interested in theory, but practical as well. And, presumably, *not*
suffering from Asperger's.

http://en.wikipedia.org/wiki/Aspergers

See, the basic structure of this ng allows for two classes: "newbies"
(aka, cannon fodder) and "regulars". You (and I) fit into neither
category. I have recently coined the term "observers" for us.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top