About -lm switch used for linking math.h

S

Stephen Sprunk

No, the logical solution is *not to compare Boolean values to true*.

"if (2)" isn't likely to occur in real code, but if you have a
variable like "ok" whose value is logically Boolean (whether it's
declared as bool, _Bool, int, or something else), then the way to
test it is simply:

if (ok) ...

Writing this:

if (ok == true) ...

make no more sense than this:

if ((ok == true) == true) ...

That much is fair. I prefer "if (ptr)" over "if (ptr!=NULL)" as well.

I'll note, though, that "if (ok!=false)" works just fine, even for
uncommon values of truth.
There are issues when you want to compare two non-constant Boolean
values to each other. For example, you might have two Boolean functions
that are supposed to compute the same result, and you want to verify
that they behave consistently. In that case, what you really want to do
is:

if (func1() == func2()) ...

which can fail if they return int, and one of them returns 1 while the
other returns 2 (both true values). In that case, you need some
additional logic to avoid that kind of problem -- such as:

if ((bool)func1() == (bool)func2()) ...

That's just working around a bug: the functions return int instead of
returning bool.
(assuming your compiler gets this right), or:

if (!!func1() == !!func2()) ...

or "if (!func1() == !func2())". I've done that before.
But for most uses of _Bool, or bool, or even int used to hold logically
Boolean values, it's easy enough to write your code in a way that (a) is
straightforward, and (b) works correctly whether there are multiple true
values or not.

I agree, but that is a skill that we had to learn _because_ C didn't
have a real boolean type. Perhaps it was so long ago for you that you
don't remember it, but I do. And it's an obvious source of bugs,
particularly when a function only rarely returns a value other than 1
for truth.

I prefer that the language match the meaning as closely as possible, so
that my code is self-documenting, which often happens to enable more
aggressive compiler optimization as a side effect.

Do you eschew unsigned types because signed ones are good enough and
_you_ know that a particular variable will never be negative, even if
the compiler doesn't? No, because you're used to them and know that, in
certain cases, unsigned types work better. I assert the same would be
true if you were used to having a true boolean type.

S
 
S

Stefan Ram

Stephen Sprunk said:
I prefer that the language match the meaning

In this world there is no meaning, there only are languages.¹

So, when someone says »the meaning«, I translate this to
»my mother language« (or »the language I am most used to
in this context«).

For example, for an english native, »the meaning« of the
italian word »casa« is »house«. But actually this is not
more of a meaning than »casa«, it is just a translation
from one (less known) language into another (more familiar).
 
K

Keith Thompson

Stephen Sprunk said:
That much is fair. I prefer "if (ptr)" over "if (ptr!=NULL)" as well.

I don't, though that's more a matter of taste. My rationale, is that
"ptr" is not what I call "logically Boolean"; it has meaningfully
distinct "true" values.
I'll note, though, that "if (ok!=false)" works just fine, even for
uncommon values of truth.

Yes, but "if (!ok)" is clearer and, IMHO, better.

[snip]
I agree, but that is a skill that we had to learn _because_ C didn't
have a real boolean type. Perhaps it was so long ago for you that you
don't remember it, but I do. And it's an obvious source of bugs,
particularly when a function only rarely returns a value other than 1
for truth.

I learned Pascal before I learned C. Even in Pascal, with a built-in
Boolean type and predefined constants True and False, comparing a value
for equality to True didn't make sense. It would work correctly, but
the "= True" would just be redundant.

In most cases, good clean code happens to be code that works well with
pre-1999 C's treatment of boolean values.
I prefer that the language match the meaning as closely as possible, so
that my code is self-documenting, which often happens to enable more
aggressive compiler optimization as a side effect.

Do you eschew unsigned types because signed ones are good enough and
_you_ know that a particular variable will never be negative, even if
the compiler doesn't? No, because you're used to them and know that, in
certain cases, unsigned types work better. I assert the same would be
true if you were used to having a true boolean type.

I certainly don't hesitate to use a true boolean type in languages that
have such a type -- except perhaps when such a type was added recently
enough that existing code doesn't use it.

(Unsigned types can have their own pitfalls. The lower bound of the
type's full range is 0, which is a common value, making it easy to wrap
to very large values if you're not careful. For a sufficiently large
signed type, the bounds are far away from any reasonable values you're
dealing with, so you can (with care) treat it as unbounded. But that's
another discussion.)
 
M

Malcolm McLean

Do you eschew unsigned types because signed ones are good enough and
_you_ know that a particular variable will never be negative, even if
the compiler doesn't?  No, because you're used to them and know that, in
certain cases, unsigned types work better.  I assert the same would be
true if you were used to having a true boolean type.
I would write

void payroll(EMPLOYEE *employees, int N)

if the code has to run on a 16 bit int machine, it's most unlikely
that there would be enough memory to store more than 32767 employees

N is of course naturally unsigned. But unsigned adds visual clutter.
size_t is now the official preferred way, but it's confusing, because
N is a count, not a size.
Then say for some reason we want to step backwards through the array.

for(i=N-1;i>=0;i--)

is the obvious way to do it. It only works if i and N are signed. If
we want to revert to stepping forwards through the array, we just
rewrite the for control.
If N is unsigned we have to make i unsigned which means that we can't
reuse i in a signed context. It also means that we have to write
something like
i = N;
while(i--)

now if we want to change to stepping forwards, the programmer has to
follow through the logic. Of course it's still a standard programming
task, but it's a bit harder, and more likely to introduce a bug.
 
B

Ben Bacarisse

Malcolm McLean said:
I would write

void payroll(EMPLOYEE *employees, int N)

if the code has to run on a 16 bit int machine, it's most unlikely
that there would be enough memory to store more than 32767 employees

N is of course naturally unsigned. But unsigned adds visual clutter.
size_t is now the official preferred way, but it's confusing, because
N is a count, not a size.
Then say for some reason we want to step backwards through the array.

for(i=N-1;i>=0;i--)

is the obvious way to do it. It only works if i and N are signed. If
we want to revert to stepping forwards through the array, we just
rewrite the for control.

In my opinion, a better way to do this is to always run the loop forward
and to transform the index in the loop body. This localises the changes
needed in one place and makes the loop robust against index type
changes:

for (i = 0; i < N; i++) {
size_t element = N - 1 - i;
/* ... */
}

You can even do this retrospectively without altering the loop body at
the expense of some yuckery:

for (i = 0; i < N; i++) {
size_t oi = i, i = N - 1 - oi; /* run the loop backwards */
/* ... */
}

Finally, rather than map the index, it's sometimes worthwhile mapping to
an element pointer:

for (i = 0; i < N; i++) {
EMPLOYEE *employee = &employees[N - 1 - i];
/* ... */
}

If this is the case, flipping the order by adjusting the index
expression is perfectly natural.

<snip>
 
K

Kaz Kylheku


I'm well aware of this and have always believe that it is completely moronic.

Firstly, POSIX already has feature test macros so that old code does not see
new identifiers. If some foo_t did not exist in 1995 POSIX, and I want to use
1995 POSIX, there is feature test macro for that so that even if 2012 there is
a foo_t, it will not be seen in my program.

Secondly, POSIX could add new identifiers at any time into headers, in ANY name
space. What's the point of saying _t is reserved, when tomorrow you could
introduce an identifier blorch, without previously having declared that
bl*ch is a reserved space? See point one: a properly versioned feature test
macro will eliminate blorch.

Thirdly, why reserve a commmon fucking space like _t that's part of a
widespread C programming convention?

Feature test macros eliminate the need for reserved spaces like this. All you
need are the generic ones like _[A-Z]* for the internal bits.
You don't need _t for header file internals.
 
T

Tim Rentsch

James Kuyper said:
On 01/27/2012 06:19 PM, Stephen Sprunk wrote:
...

The behavior of _Bool is different from any other unsigned integer type
in two ways:
* It has an integer conversion rank lower than any other standard
integer type.
* (_Bool)expression is equivalent to ((expression) != 0).

And (3), assigning a pointer type to a _Bool is allowed
without having to use a cast.
 
T

Tim Rentsch

Malcolm McLean said:
Do you eschew unsigned types because signed ones are good enough and
_you_ know that a particular variable will never be negative, even if
the compiler doesn't? No, because you're used to them and know that, in
certain cases, unsigned types work better. I assert the same would be
true if you were used to having a true boolean type.
I would write

void payroll(EMPLOYEE *employees, int N)

if the code has to run on a 16 bit int machine, it's most unlikely
that there would be enough memory to store more than 32767 employees

N is of course naturally unsigned. But unsigned adds visual clutter.
[snip unrelated]

Forgive me for being blunt, but that's a moronic statement.
There isn't any clutter in using 'unsigned' any more than
there would be for 'long' or 'float' or 'double'. All of
these express a basic type in a single English word. Are they
all longer than 'int'? Sure. Do they have any extraneous or
redundant parts (ie, clutter)? They do not.
 
J

Joshua Maurice

Which is one reason why C++ has specialised containers for bool.  In
some ways bool sits better in C++ than in C (it has always been part of
the language), but then again just about every C project I've worked on
has some form of boolean type defined.  So adding _Bool to the language
does follow the standardisation mantra of standardising existing practice..

Really? I thought this was considered a headache in C++, and there was
talk of deprecating the template specialization of std::vector<bool>
due to its various headaches. It really would have been much better to
introduce a specific type like std::bit_vector instead of specializing
and thereby changing the semantics of std::vector<bool>.
 
J

Joe keane

Which is one reason why C++ has specialised containers for bool.

Which is more of a headache than useful.

A bit is not an object, it doesn't have an address.

Just make 'bitstring' and done-with-it.
 
J

Jorgen Grahn

[...] but then again just about every C project I've worked on
has some form of boolean type defined.

I even knew of a project that had four different boolean types. One of
which had TRUE, FALSE and MAYBE.

Seen that many times (not the MAYBE). Sometimes they are designed so
that 'if(my_boolean)' doesn't work as expected.

Add to that various status code/return code types/defines and the
various conventions for using int return codes (e.g. 0 is success and
-1 failure; OK and ERROR are defined in various ways), and you have
great opportunities for broken error handling :-(

/Jorgen
 
P

Phil Carmody

Kleuske said:
In what universe is MS-DOS "unix-based?".

Holy moley. If his posts are going to be that hilariously stupid,
I might consider pulling him out of the killfile. Or is tha above
just a one-off, in which case thanks for responding to it so we
could all have a laugh at the pseudonymous twerp's expense?

Phil
 
P

Phil Carmody

Ike Naar said:
(ok) is still clearer and still better.

Except when it isn't. For example the commonly adopted coding style
for the linux kernel is to have desirable operation flow uninterupted
down a function, and for failures to jump away from that flow (be that
goto, break, return, whatever).

Phil
 
I

Ike Naar

Except when it isn't. For example the commonly adopted coding style
for the linux kernel is to have desirable operation flow uninterupted
down a function, and for failures to jump away from that flow (be that
goto, break, return, whatever).

Even under such a coding style, "!ok" means the opposite of "ok!=false".
 
K

Keith Thompson

Ike Naar said:
Even under such a coding style, "!ok" means the opposite of "ok!=false".

Yes, that was my mistake, which I acknowleged when it was first pointed out.
 
P

Phil Carmody

Ike Naar said:
Even under such a coding style, "!ok" means the opposite of "ok!=false".

Ack. I only read context if the new material doesn't make sense -
my key-hole approach gave me an inappropriate context. Apologies.

Phil
--
I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
474,082
Messages
2,570,589
Members
47,211
Latest member
Shamestone

Latest Threads

Top