[Q] UB query

S

Simon P Simonson

In the following code,

1 main()
2 {
3 auto a;
4 volatile b;
5 (void) a;
6 (void) b;
7 }

is line 5 an undefined behavior? (I would say NO)

is line 6 an undefined behavior? (I would say YES)

Merry Xmas
SPS
 
K

Keith Thompson

Simon P Simonson said:
In the following code,

1 main()
2 {
3 auto a;
4 volatile b;
5 (void) a;
6 (void) b;
7 }

is line 5 an undefined behavior? (I would say NO)

is line 6 an undefined behavior? (I would say YES)

First off, the code is written in an extremely archaic style; it's
not even legal in C99. In older versions of C, you could omit the
type on a declaration and it would default to int. There's no good
reason to do that.

int main(void)
{
int a;
volatile int b;
(void) a; /* line 5 */
(void) b; /* line 6 */
return 0;
}

I'd say yes to both. It's possible for type int to have trap
representations. If it does, and if ``a'' happens to hold a trap
representation, then the behavior of an attempt to access the value of
``a'' is undefined.
 
B

Barry Schwarz

In the following code,

1 main()
2 {
3 auto a;
4 volatile b;
5 (void) a;
6 (void) b;
7 }

is line 5 an undefined behavior? (I would say NO)

is line 6 an undefined behavior? (I would say YES)

An uninitialized variable without static duration has an indeterminate
value. Any attempt to evaluate such a value leads to undefined
behavior. The fact that you discard the result of the evaluation is
irrelevant. Both statements invoke undefined behavior.
 
N

Nobody

In the following code,

1 main()
2 {
3 auto a;

What's with the "auto" qualifier?

In over 20 years of C programming, I haven't even seen it once in
real-world code.

It's primary purpose seems to be to trip up programmers who don't play
language lawyer as a hobby. I.e.:

Step 1: write:

int auto = 0; /* do "it" automatically */

Step 2: spend 5 minutes wondering why the compiler's complaining about
"syntax error before '=' token".

Step 3: discover that changing it to:

int xauto = 0; /* do "it" automatically */

eliminates the error message.

Step 4: ask the resident language lawyer about "auto".

Step 5: end up spending the rest of the afternoon discussing punched
cards, EBCDIC, and JCL.
 
S

Simon P Simonson

Barry said:
An uninitialized variable without static duration has an indeterminate
value. Any attempt to evaluate such a value leads to undefined
behavior. The fact that you discard the result of the evaluation is
irrelevant. Both statements invoke undefined behavior.

Are you sure about this analysis?

Code 1 <<EOF
auto a;
(void) a;
EOF

Code 2 <<EOF
auto a;
EOF

By the "as if" rule, these two code extracts are equivalent, and most
(all?) compilers will not generate any code for Code 1 following the "as
if" rule.
 
S

Simon P Simonson

Nobody said:
What's with the "auto" qualifier?

In over 20 years of C programming, I haven't even seen it once in
real-world code.

It's primary purpose seems to be to trip up programmers who don't play
language lawyer as a hobby. I.e.:

Step 1: write:

int auto = 0; /* do "it" automatically */

Step 2: spend 5 minutes wondering why the compiler's complaining about
"syntax error before '=' token".

Step 3: discover that changing it to:

int xauto = 0; /* do "it" automatically */

eliminates the error message.

Step 4: ask the resident language lawyer about "auto".

Step 5: end up spending the rest of the afternoon discussing punched
cards, EBCDIC, and JCL.

Obviously I used auto to emphasize and draw attention to the contrast
between volatile and non-volatile variables that was at the heart of my
question. Normally I don't use the auto qualifier in my code.
 
S

Simon P Simonson

Simon said:
Are you sure about this analysis?

Code 1 <<EOF
auto a;
(void) a;
EOF

Code 2 <<EOF
auto a;
EOF

By the "as if" rule, these two code extracts are equivalent, and most
(all?) compilers will not generate any code for Code 1 following the "as
if" rule.

Actually this raises the interesting question: What code should a
compiler generated for Code 3?

Code 3 <<EOF
volatile a;
(void) a;
EOF
 
S

Simon P Simonson

Simon said:
Obviously I used auto to emphasize and draw attention to the contrast
between volatile and non-volatile variables that was at the heart of my
question. Normally I don't use the auto qualifier in my code.

This also raise the iteresting question: Is it a good idea to use
slightly obscure language features in your code (e.g. sizeof a++) to
force future maintainers of your code to be up to scratch on the dark
corners of the language?
 
T

Tom St Denis

This also raise the iteresting question: Is it a good idea to use
slightly obscure language features in your code (e.g. sizeof a++) to
force future maintainers of your code to be up to scratch on the dark
corners of the language?

No, if I caught a developer here writing things like "sizeof a++" I'd
make them clean it up [or I'd do it myself and commit the fix].

Hard to read code is not the sign of genius. It's the sign of
lazyness. Using extra parenthesis for example may mean you're not
100% sure of order of precedence [bad] but it also makes the code a
lot easier to read [good].

Tom
 
N

Nick

Simon P Simonson said:
This also raise the iteresting question: Is it a good idea to use
slightly obscure language features in your code (e.g. sizeof a++) to
force future maintainers of your code to be up to scratch on the dark
corners of the language?

Only if it's the "best" way to do things. Even if the future maintainer
is you, there's no point in making things complicated just for the fun
of it.

I feel that quite strongly about a number of the "is this undefined
behaviour" threads we get. If after 3 days people are still arguing
about it, just don't use it!
 
B

Ben Bacarisse

Barry is correct from the point of view of portable code, but an
indeterminate value is always either a valid (but unknown) value of
the type in question or it is a trap representation. On a system
whose int type has no trap representations, the effect of evaluating
an indeterminate value is not undefined (in the C sense of the word).
Of course, what value you get is not specified so the effect is
"undefined" in the ordinary sense of the word.

The "as if" rule can't be used to make undefined code defined. On a
system with an int type that has trap reps., a compiler can replace
Code 1 with anything it likes. If int has no trap reps., then Code 1
is defined, and the compiler can replace it with Code 2 because it is
equivalent.
Actually this raises the interesting question: What code should a
compiler generated for Code 3?

Code 3 <<EOF
volatile a;
(void) a;
EOF

Pass. On the face of it, it must generate code to access a, but there
may be ways in which it can prove that this is not required.
 
K

Keith Thompson

Simon P Simonson said:
Are you sure about this analysis?

Code 1 <<EOF
auto a;
(void) a;
EOF

Code 2 <<EOF
auto a;
EOF

By the "as if" rule, these two code extracts are equivalent, and most
(all?) compilers will not generate any code for Code 1 following the "as
if" rule.

The "as if" rule doesn't say they're equivalent. It says that
an implementation is allowed to eliminate the evaluation of "a",
transforming Code 1 to the equivalent of Code 2. It's not *required*
to eliminate it.

If "a" happens to contain a trap representation, and the compiler
doesn't optimize away the evaluation, then the program could crash.
(Accessing a trap representation doesn't necessarily cause a crash,
but that's one of the infinitely many possible consequences of
undefined behavior.)

A compiler is allowed to *assume* that no undefined behavior occurs,
and generate code based on that assumption. This is most commonly
shows up in optimization, but a compiler could emit extra checks.
For example, it might generate code that checks whether any variables
are used without being initialized, and that terminates the program
with an error message if the check fails. (Create a flag for
each declared variable. Set it to 1 when the variable has a value
assigned to it. Check the flag whenever the variable is accessed.
Or there might be hardware support.)
 
K

Keith Thompson

Simon P Simonson said:
Obviously I used auto to emphasize and draw attention to the contrast
between volatile and non-volatile variables that was at the heart of my
question. Normally I don't use the auto qualifier in my code.

Here's the code again:

1 main()
2 {
3 auto a;
4 volatile b;
5 (void) a;
6 (void) b;
7 }

(Incidentally, posting code with line numbers makes it more difficult
for the rest of us to copy-and-paste it and try it ourselves. If you
want to refer to a line by number, add a "/* line 5 */ comment.)

"auto" is a storage-class specifier; "volatile" is a type qualifier.
Both "a" and "b" have the same storage class. If you wanted to be
painfully explicit, you could have written:

auto int a;
volatile auto int b;

This:

int a;
volatile int b;

would have been clearer and would have made your point better.
Omitting the type, and letting it default to int, is considered
poor style in C90, and isn't even allowed in C99.
 
S

Simon P Simonson

Ben said:
The "as if" rule can't be used to make undefined code defined. On a
system with an int type that has trap reps., a compiler can replace Code
1 with anything it likes. If int has no trap reps., then Code 1 is
defined, and the compiler can replace it with Code 2 because it is
equivalent.


Pass. On the face of it, it must generate code to access a, but there
may be ways in which it can prove that this is not required.

OK, but what does it mean to access a? If a is stored in a memory
location (possibly memory mapped hardware) is it enough to load a into a
register? What if a is already in cache?
 
B

Barry Schwarz

Are you sure about this analysis?

Code 1 <<EOF
auto a;
(void) a;
EOF

Code 2 <<EOF
auto a;
EOF

By the "as if" rule, these two code extracts are equivalent, and most
(all?) compilers will not generate any code for Code 1 following the "as
if" rule.

You didn't ask if the generated code would be well behaved on most
systems. What about turning ALL optimization off.

You used the standard specific term "undefined behavior" so I assumed
you wanted to know what the standard specified.

I guess I could have been a bit more precise and said the behavior is
undefined at the point the value is evaluated.
 
F

Flash Gordon

Simon said:
OK, but what does it mean to access a?

That is implementation defined...
If a is stored in a memory
location (possibly memory mapped hardware) is it enough to load a into a
register? What if a is already in cache?

The documentation for the implementation should tell you what it does,
since implementation defined means it has to be documented. In most
implementations it will do something sensible, and if you need to do
more than just use volatile it will tell you what, but that is a matter
of quality rather than requirement.
 
K

Keith Thompson

Simon P Simonson said:
OK, but what does it mean to access a? If a is stored in a memory
location (possibly memory mapped hardware) is it enough to load a into a
register? What if a is already in cache?

Here's what the standard says (C99 6.7.3p6):

An object that has volatile-qualified type may be modified
in ways unknown to the implementation or have other unknown
side effects. Therefore any expression referring to such an
object shall be evaluated strictly according to the rules of
the abstract machine, as described in 5.1.2.3. Furthermore,
at every sequence point the value last stored in the object
shall agree with that prescribed by the abstract machine, except
as modified by the unknown factors mentioned previously. What
constitutes an access to an object that has volatile-qualified
type is implementation-defined.

with a footnote:

A volatile declaration may be used to describe an object
corresponding to a memory-mapped input/output port or an object
accessed by an asynchronously interrupting function. Actions
on objects so declared shall not be ‘‘optimized out’’
by an implementation or reordered except as permitted by the
rules for evaluating expressions.

On the other hand, if you just apply "volatile" to a local variable,
the compiler can reasonably know that there's no magic going on behind
the scenes. It's not clear whether the compiler is permitted to take
advantage of this knowledge; I'd say it probably isn't.
 
T

Tim Rentsch

Ben Bacarisse said:
Barry is correct from the point of view of portable code, but an
indeterminate value is always either a valid (but unknown) value of
the type in question or it is a trap representation. On a system
whose int type has no trap representations, the effect of evaluating
an indeterminate value is not undefined (in the C sense of the word).
Of course, what value you get is not specified so the effect is
"undefined" in the ordinary sense of the word.

This was covered (IIRC) in one of the DR's. I believe the
behavior is undefined even if the type in question has no
trap representations, eg, even (unsigned char) can be
undefined behavior. That doesn't hold (again IIRC) for
variables whose address has been taken with &, and I'm
not sure if a variable declared 'volatile' is different.
 
T

Tim Rentsch

Keith Thompson said:
Here's what the standard says (C99 6.7.3p6):

An object that has volatile-qualified type may be modified
in ways unknown to the implementation or have other unknown
side effects. Therefore any expression referring to such an
object shall be evaluated strictly according to the rules of
the abstract machine, as described in 5.1.2.3. Furthermore,
at every sequence point the value last stored in the object
shall agree with that prescribed by the abstract machine, except
as modified by the unknown factors mentioned previously. What
constitutes an access to an object that has volatile-qualified
type is implementation-defined.

with a footnote:

A volatile declaration may be used to describe an object
corresponding to a memory-mapped input/output port or an object
accessed by an asynchronously interrupting function. Actions
on objects so declared shall not be ''optimized out''
by an implementation or reordered except as permitted by the
rules for evaluating expressions.

On the other hand, if you just apply "volatile" to a local variable,
the compiler can reasonably know that there's no magic going on behind
the scenes. It's not clear whether the compiler is permitted to take
advantage of this knowledge; I'd say it probably isn't.

The whole point of volatile is that the compiler does NOT know
and must not assume that it does, even for local variables.
 
B

Ben Bacarisse

Tim Rentsch said:
This was covered (IIRC) in one of the DR's. I believe the
behavior is undefined even if the type in question has no
trap representations, eg, even (unsigned char) can be
undefined behavior.

Do you have a reference? A DR that modifies what seems to be the
clear wording of standard, it usually ends with an edit to the wording
which should be reflected in the drafts. I don't understand the
process that well, so I may have got this wrong. The text of the DR
would help.

<snip>
 

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,744
Messages
2,569,480
Members
44,900
Latest member
Nell636132

Latest Threads

Top