Is goto Still Considered Harmful?

K

Keith Thompson

James Van Buskirk said:
At last! A C programmer to inflict the Question on! Now, what if
true is a const _Bool with internal representation B'00000010'
and var is also an _Bool. Does the assert happen if NDEBUG is not
on?

Since `var = true` is an assignment, yes, the assert will fire.

Let's assume that the `= true` is replaced by `== true` (though it would
be better to omit it altogether and write `assert(var);`).

If there's a visible `#include <stdbool.h>` directive, then `true` is a
macro that expands to 1, so to satisfy your assumptions we have to
assume that the programmer has (foolishly) redefined `true` -- but how?

Converting any scalar value to _Bool yields 0 if the value is equal to
0, 1 if it's not, which makes it difficult to construct a _Bool value
other than 0 or 1.

If you manage do so, it's likely that your program's behavior has become
undefined while doing so, which means the assert happen, or it may not
happen, or the walls might start to bleed.

The simple answer is: Don't do that.

But if you want an answer beyond that, I suggest you show the specific C
code you're asking about.

(Incidentally, B'00000010' is not C syntax, but it's obvious enough what
you mean.)
 
K

Kaz Kylheku

["Followup-To:" header set to comp.lang.c.]
At last! A C programmer to inflict the Question on! Now, what if
true is a const _Bool with internal representation B'00000010'
and var is also an _Bool. Does the assert happen if NDEBUG is not
on?

The code generated by the assert macro, when NDEBUG is not present, should not
treat this expression any differently than something like

if (var = true) ...

The assignment must happen if NDEBUG is absent, and its result tests as
true.
 
G

glen herrmannsfeldt

(snip)
(Incidentally, B'00000010' is not C syntax, but it's obvious
enough what you mean.)

It is, as far as I know, OS/360 (and successor) assembler syntax.

I more often use the hex constant X'123' syntax when describing
hex constants in a language independent context.

PL/I syntax for binary constants is 00000010B (binary point is
allowed for scaled fixed binary). Leading zeros determine the
precision (bit width) of the constant, which sometimes matters.

Do newer C standards have a syntax for binary constants?

-- glen
 
J

James Van Buskirk

Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]

Posted to clf because the nature of the question is central to
The code generated by the assert macro, when NDEBUG is not present, should
not
treat this expression any differently than something like
if (var = true) ...
The assignment must happen if NDEBUG is absent, and its result tests as
true.

Yes, this last line of code above is perhaps a better illustration
of the issue. My impression was that assignment between variables
of the same integer type in C was to copy all the bits, and that
the result of the assignment was the value that the variable got
assigned.

But what is that value in this case? If the low bit of an _Bool
is the only significant bit, then the value of B'00000010' is 0;
if at least the two low bits are significant, then the value is
2, which, being nonzero, is true, and if any value but
B'00000000' and B'00000001' is a trap value, then the value is
undefined. It seems to me that C99 allows the compiler to
choose any of these behaviors, or does it in fact require one
of them rather than the others?
 
G

glen herrmannsfeldt

(snip, I wrote)
AFAIK, the COMEFROM "statement" was from a humorous Datamation article
published in the late 70s during much of the structure programming
debate. Makes me miss the old "Fortran Man" cartoons in that
publication...

When I was in high school, so about 1975, I used to sometimes
read Datamation in our city library. I think that is when I
saw the one about the PL/I compiler.

-- glen
 
J

James Kuyper

Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]

Posted to clf because the nature of the question is central to
The code generated by the assert macro, when NDEBUG is not present, should
not
treat this expression any differently than something like
if (var = true) ...
The assignment must happen if NDEBUG is absent, and its result tests as
true.

Yes, this last line of code above is perhaps a better illustration
of the issue. My impression was that assignment between variables
of the same integer type in C was to copy all the bits, ...

Not quite - the value of the right operand of an assignment expression
is determined (a process that may involve the usual arithmetic
conversions), and is then converted to the destination type (if
different). A representation of that value in the destination type is
then placed in the destination object. For any type where there are two
or more different ways to represent the same value, there's no guarantee
as to which of those ways gets chosen; it needn't be the same as the
representation used in the source object, even if they have exactly the
same type. Even if the value and sign bits are the same, the values of
the padding bits (if any) can be completely different.
... and that
the result of the assignment was the value that the variable got
assigned.

But what is that value in this case? If the low bit of an _Bool
is the only significant bit, then the value of B'00000010' is 0;
if at least the two low bits are significant, then the value is
2, which, being nonzero, is true, and if any value but
B'00000000' and B'00000001' is a trap value, then the value is
undefined. It seems to me that C99 allows the compiler to
choose any of these behaviors, or does it in fact require one
of them rather than the others?

A _Bool is only required to be able to store two values: 0 and 1, a
requirement that can be met by having 1 value bit and 7 padding bits.
Whether _Bool can store larger values is unspecified. Larger values
can't be assigned to a _Bool object, because conversion to _Bool is part
of the assignment process. Such values, if they can be stored, could
only be created by type punning.
The standard also doesn't specify that any particular bit must be the
value bit - in principle, the single bit that is set in B'00000010'
could be the only bit used by _Bool, though that seems unlikely.
 
K

Kaz Kylheku

["Followup-To:" header set to comp.lang.c.]
The most elegant control flow statement has to be the
AT statement (equivalent to COME FROM).

Consider the beauty of

K = 3
20 I = K

and, somewhere quite far from this,

AT 20
K = 5
END

Given that this was on punched cards, it seems it would have been much smarter
to just insert a K = 5 card after K = 3 into the deck.

And maybe do something to make these debugging cards easier to find and later
remove, like some kind of marking (red blot of ink on the edge or whatever).
 
K

Kaz Kylheku

["Followup-To:" header set to comp.lang.c.]
Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]

Posted to clf because the nature of the question is central to
The code generated by the assert macro, when NDEBUG is not present, should
not
treat this expression any differently than something like
if (var = true) ...
The assignment must happen if NDEBUG is absent, and its result tests as
true.

Yes, this last line of code above is perhaps a better illustration
of the issue. My impression was that assignment between variables
of the same integer type in C was to copy all the bits, and that
the result of the assignment was the value that the variable got
assigned.

Importantly, the result of the assignment also has the type of the left hand
side variable. The value of (var = true) has the type of var,
and the post-assignment value.

Secondly, assert is not a function; the asserted value is not coerced
into some particular type like int, or bitwise reinterpreted or whatever.
But what is that value in this case? If the low bit of an _Bool
is the only significant bit, then the value of B'00000010' is 0;
if at least the two low bits are significant, then the value is
2, which, being nonzero, is true, and if any value but
B'00000000' and B'00000001' is a trap value, then the value is
undefined. It seems to me that C99 allows the compiler to
choose any of these behaviors, or does it in fact require one
of them rather than the others?

I don't see how (var = true) can have a portability issue, where
var is of type bool, and the result is not converted in any way,
but only used as a control expression for some conditional thing.
 
G

glen herrmannsfeldt

(snip)
Given that this was on punched cards, it seems it would have
been much smarter to just insert a K = 5 card after K = 3
into the deck.

One could do that. Though I wrote most of my Fortran G programs
on WYLBUR, and not on cards, but that is a different question.

Now, should the compiler enforce good practices? I have more
complaints about Fortran trying to force style, rarely C.

Sometimes I might want to change something for a quick test,
but not yet change an otherwise working program. To be sure that
I could get back to the original again. Now we can just copy the
file to a temporary, but not so easy almost 50 years ago.
And maybe do something to make these debugging cards easier to
find and later remove, like some kind of marking (red blot
of ink on the edge or whatever).

I suppose, but could you be sure to get every last one out later?

Now we can #ifdef DEBUG and be sure to not define DEBUG in the
production version.

-- glen
 
D

David Brown

Nope. One would think a trivial to implement addition, breaking no
existing code, with many requests, would get at least a little
attention from the C committee...

gcc has binary literals such as 0b00100001 as an extension. Since it is
in popular use, has an existing implementation, and no signs of
conflicting with existing code, it is probably a good candidate for
future C standards.

And although it is off-topic in both C and Fortran groups, 0b001 style
binary literals are due to be in C++14.
 
K

Kaz Kylheku

gcc has binary literals such as 0b00100001 as an extension. Since it is
in popular use, has an existing implementation, and no signs of
conflicting with existing code, it is probably a good candidate for
future C standards.

But of course, the imbeciles will, again, do it in a completely different way
from GCC, like inline and variadic macros, so then, in turn, GCC will have GNU
boolean constants (warned about, if not outright disabled, in -pedantic mode),
and ISO boolean constants.
 
T

Thomas Jahns

What really puzzles me about this bug is that pretty much all C/C++
compilers for large systems (and certainly GCC), will issue an
"unreachable code" warning at all but the minimal warning level. At
least around here the policy is compile at a high warning level, and
treat warnings as errors, not doing something like that, especially in
security critical code, just seems sloppy.

http://gcc.gnu.org/ml/gcc-help/2011-05/msg00360.html

so gcc won't warn. Apple uses clang, there the option is not part of -Wall and
therefore easily missed. I'd also advocate using it, because it very often
indicates a block of code improperly compiled (e.g. because of missing #ifdef's).

Thomas
 
D

David Brown

But of course, the imbeciles will, again, do it in a completely different way
from GCC, like inline and variadic macros, so then, in turn, GCC will have GNU
boolean constants (warned about, if not outright disabled, in -pedantic mode),
and ISO boolean constants.

That's possible - the C and C++ committees have an annoying habit of
doing things differently without reasons that are comprehensible to us
mere mortals. But in this case I think there really are no other ways
to implement binary literals in a manner consistent with existing C. So
I do hope they will be included in this form in later standards.
 
J

James Van Buskirk

On 03/13/2014 11:40 PM, James Van Buskirk wrote:
Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]
Posted to clf because the nature of the question is central to
an issue due to Fortran interoperability (_Bool <-> LOGICAL(C_BOOL)).
At last! A C programmer to inflict the Question on! Now, what if
true is a const _Bool with internal representation B'00000010'
and var is also an _Bool. Does the assert happen if NDEBUG is not
on?
The code generated by the assert macro, when NDEBUG is not present,
should
not
treat this expression any differently than something like
if (var = true) ...
The assignment must happen if NDEBUG is absent, and its result tests as
true.
Yes, this last line of code above is perhaps a better illustration
of the issue. My impression was that assignment between variables
of the same integer type in C was to copy all the bits, ...
Not quite - the value of the right operand of an assignment expression
is determined (a process that may involve the usual arithmetic
conversions), and is then converted to the destination type (if
different).

Part of the premise of this subthread is that both left and right
operands have the same intrinsic type, _Bool. See above.
In n1124.pdf, section 6.3 I read:

"Conversion of an operand value to a compatible type causes no
change to the value or the representation."

Doesn't that mean that the compiler is just supposed to copy
bits?
A representation of that value in the destination type is
then placed in the destination object. For any type where there are two
or more different ways to represent the same value, there's no guarantee
as to which of those ways gets chosen; it needn't be the same as the
representation used in the source object, even if they have exactly the
same type. Even if the value and sign bits are the same, the values of
the padding bits (if any) can be completely different.

As noted above, this is inconsistent with my understanding of
the passage quoted above.
A _Bool is only required to be able to store two values: 0 and 1, a
requirement that can be met by having 1 value bit and 7 padding bits.
Whether _Bool can store larger values is unspecified. Larger values
can't be assigned to a _Bool object, because conversion to _Bool is part
of the assignment process.

But it seems to me that they can when assigning from _Bool to
_Bool.
Such values, if they can be stored, could
only be created by type punning.

Creating the first _Bool with a value outside the set {0,1}
in an assignment chain is, of course, another matter entirely,
but there are several ways it can happen. Painfully, it can
happen in pre-MIL-STD 1753 Fortran code which can be broken
in a newfangled compiler that supports C_BOOL /= -1.
 
J

James Van Buskirk

Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]
I don't see how (var = true) can have a portability issue, where
var is of type bool, and the result is not converted in any way,
but only used as a control expression for some conditional thing

All this stuff with the assignment seems to be clouding the
central issue of this subthread, which is that if var has type
_Bool and internal representation B'00000010' what happens
when

if(var)
{
x = 1;
}
else
{
x = 0;
}

is executed? Does the standard allow bits 1:7 to be padding
bits so that var tests as false and x = 0, or does it allow
all bits to be value bits so that var, being nonzero, tests
as true so x = 1, or does it allow B'00000010' to be a trap
value so that exceptional behavior can happen? Is any of
these behaviors required?
 
B

Ben Bacarisse

James Van Buskirk said:
Part of the premise of this subthread is that both left and right
operands have the same intrinsic type, _Bool. See above.
In n1124.pdf, section 6.3 I read:

"Conversion of an operand value to a compatible type causes no
change to the value or the representation."

Doesn't that mean that the compiler is just supposed to copy
bits?

That would be down to the definition of the assignment operator. The
conversion causes no change, but I can't see any guarantee that the
assignment won't. Specifically, assignment is described as storing a
value, and that might involve changing the representation if the
implementation wants to do that.

And implementations do seem to vary. This program:

#include <stdio.h>

int main(void)
{
_Bool b1, b2;
*(char *)&b1 = 2;
b2 = b1;
printf("%d\n", *(char *)&b2);
}

prints 2 when compiled with gcc and 0 when compiled with clang. Both
outputs (along with many others!) are permitted.

But it seems to me that they can when assigning from _Bool to
_Bool.

Yes, they can, but they don't have to.

<snip>
 
B

Ben Bacarisse

James Van Buskirk said:
Kaz Kylheku said:
["Followup-To:" header set to comp.lang.c.]
I don't see how (var = true) can have a portability issue, where
var is of type bool, and the result is not converted in any way,
but only used as a control expression for some conditional thing

All this stuff with the assignment seems to be clouding the
central issue of this subthread, which is that if var has type
_Bool and internal representation B'00000010' what happens
when

if(var)
{
x = 1;
}
else
{
x = 0;
}

is executed? Does the standard allow bits 1:7 to be padding
bits so that var tests as false and x = 0,
Yes.

or does it allow
all bits to be value bits so that var, being nonzero, tests
as true so x = 1,

This one is weird. The trouble is you can't tell if _Bool has more than
one value bit. You can certainly set more than the "proper" bit by type
punning, but there is no way to know if what you see is actually a
result of there being more than one value bit or the result of undefined
behaviour caused by have used a trap representation.
or does it allow B'00000010' to be a trap
value so that exceptional behavior can happen?
Yes.

Is any of
these behaviors required?

No, and that's really the main point. In testing, I even found a case
where if (var) ... behaves differently to (var ? ... : ...) which is
entirely conforming since all bets are off once you've set padding bits.
 
J

James Kuyper

Part of the premise of this subthread is that both left and right
operands have the same intrinsic type, _Bool. See above.
In n1124.pdf, section 6.3 I read:

n1124.pdf is quite old; I'm not sure which version of the standard it
describes. I don't think anything relevant to this discussion has
changed recently, but uou should still get n1570.pdf, which is almost
identical to the current standard.
"Conversion of an operand value to a compatible type causes no
change to the value or the representation."

Doesn't that mean that the compiler is just supposed to copy
bits?

The conversion may not change the representation (which matters for the
purpose of evaluating bit-wise operators), but "Where a value is stored
in an object using a type that has more than one object representation
for that value, it is unspecified which representation is used, ..."
(6.2.6.1p8). For _Bool, this could only apply to the padding bits (if
any), but you were making a more general statement about "assignment
between variables of the same integer type in C", so it could also apply
to signed types, if they support negative zeros, which are considered to
have the same value as positive zeros.

....
But it seems to me that they can when assigning from _Bool to
_Bool.

True, but only if you already have a _Bool object containing such a
representation, which pushes the issue one step back, but doesn't
resolve it. Somewhere along the line it is necessary to use type punning
to create the first such object, which can then be copied around.
Creating the first _Bool with a value outside the set {0,1}
in an assignment chain is, of course, another matter entirely,
but there are several ways it can happen. Painfully, it can
happen in pre-MIL-STD 1753 Fortran code which can be broken
in a newfangled compiler that supports C_BOOL /= -1.

While this thread is cross-posted to comp.lang.fortran, but my answer
was only about C.
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top