question on const

F

Flash Gordon

On Thu, 08 Jan 2004 21:00:37 GMT

As I said above, I don't want to write crap code with those tricks in
it. I stick to the standard when writing code, or at least I try to :)
I want to write the best code I can, and be aware of *why* that kind
of code results in undefined behavior.

The *only* reason code invokes undefined behaviour is because it does
something the standard says is undefined behaviour. It is nothing to do
with what compilers do when said undefined behaviour is invoked.
This is *understanding*. The
other way is just *believing*, and that could lead to mistakes too.

Knowing what any given set of compilers and options does with undefined
behaviour does not tell you anything about undefined behaviour. It does
not even tel you why the behaviour was originally specified as
undefined since those choices where made before any compiler you are
likely to use was written.

Do you have to see someone killed by having mains electricity connected
across their heart before you believe it is a bad idea? Do you have to
write off your computer before agreeing that connecting the power supply
to the motherboard incorrectly (something which *was* possible with AT
PSUs)?
 
A

Alexander Bartolich

begin followup to tinybyte:
[...] I don't want that. I will keep programming in C for my
whole life, because I love it.

Mhh. Puts the thread into different perspective.
There are a few strategies to avoid this kind of debugging experience.

The good old days : #define SYMBOL LITERAL;
Only for limited range : enum { SYMBOL = LITERAL };
Use write-protection of OS : static const TYPE = LITERAL;
Performance be damned : TYPE SYMBOL(void) { return LITERAL; }
 
B

Ben Pfaff

Alexander Bartolich said:
The good old days : #define SYMBOL LITERAL;
Only for limited range : enum { SYMBOL = LITERAL };
Use write-protection of OS : static const TYPE = LITERAL;
Performance be damned : TYPE SYMBOL(void) { return LITERAL; }

Better performance : inline TYPE SYMBOL(void) { return LITERAL; }
 
C

Christian Bau

tinybyte said:
Compilers simply must not permit such a thing, if it gives an
undefined behavior.

"Undefined behavior" means that anything can happen, and whatever
happens is your fault (the programmers fault), and not the fault of the
compiler. And whatever the compiler does is correct: When the behavior
is undefined, then _any_ behavior that the compiler produces is correct
behavior.
 
C

Christian Bau

tinybyte said:
The compiler is allowed to assume that the value it's not changed, but
sometimes it is changed, sometimes not, depends on optimization.
This is inconsistent behavior for me, and in C programming this should
not be ALLOWED. That is all I'm talking about.

True. The programmer who wrote that code deserves a kick in the ***.
I'm trying to say that compilers aren't perfect. And that all those
standards everyone here care so much about are not of any help in this case.
A value that cannot be changed has been changed, under certain conditions.
That means something is broken.

Yes, the code that some stupid programmer wrote is broken.
Is the same as you have an application that normally gives perfect output,
but by using it in a different way it was written for, you can screw up
the whole thing. You should not be allowed to use it that way.
If you can, there is a bug.

True, some people shouldn't be allowed to write C programs.
 
K

Keith Thompson

Kevin Goodsell said:
tinybyte wrote: [...]
I'll try to explain this way:
#include <stdio.h>
int
main (void)
{
const int num = 100;
int *ip;
ip = (int *) &num;
*ip = 200;
printf ("value of num is %d(%d) \n", num, *ip);
printf ("address of ip is 0x%p\n", ip);
printf ("address of num is 0x%p\n", &num);

These last two statements invoke undefined behavior. %p is for void
pointers only. It's also a little silly to add your own 0x prefix. On
some implementations you'll get output like

0x0x034aff30

When I use "%p", I find it useful to add something to make it clear
that the value being displayed is a pointer (since the output
generated by "%p" often looks like a number). I usually just surround
it with square brackets. For example:

printf ("address of ip is [%p]\n", (void*)ip);
printf ("address of num is [%p]\n", (void*)&num);
 
J

Jack Klein

Hello,

I am sorry. After reading the post myself, I felt that the post is
incomplete in itself.

Well when I tried this exercise I expected the code to fail in the
compilation phase with an error ( not a warning). It did not, and the output

The C standard requires that a compiler issue a diagnostic for
constraint violations, of which there are none in your code. The
standard does not define "error messages" or "warning messages",
either would suffice to meet the standard's requirements for a
diagnostic.
made me think how is this possible to have 2 different value when I am
pointing to the same memory location? I exptected the output to 2 100s or 2
200s. Is 'num' variable pointing some where ????

My question is how is the output different? Is the program performing an
undefined behavior?

Yes, the code invokes undefined behavior. Attempting to change the
value of an object defined as const is undefined behavior. A compiler
is never required by the standard to issue any diagnostic for
undefined behavior.
 
J

J. J. Farrell

tinybyte said:
Yes, they should be detected, and result in an error.

That would be the ideal. A major part of the thinking behind the
concept of 'undefined behaviour' in C is that many of these
situations are extremely hard to detect - in practice, more or
less impossible in some cases with compiler technology as it was
around the time that the Standard was first created. The complexity
that would have to be added to detect all possible cases of undefined
behaviour is huge. Some would say that a compiler which added all
this error detection would have a higher quality of implementation;
others consider that this effort is better spent on improving the
compilation of correct code, perhaps by improving optimization.

The essential thinking is that the compiler can assume that coding
errors that lead to undefined behaviour never occur - it doesn't
need to consider the possibility in any way. This sometimes ends up
with the program accidentally doing exactly what the coder wanted,
but often with it misbehaving in bizarre ways. It will often do
the "right" thing if optimization is disabled, and go "wrong" when
optimized. All but one of the many instances I've looked at of "a
bug in the optimizer" have turned out to be undefined behaviour in
the code being compiled.
 
K

Keith Thompson

tinybyte said:
Yes, they should be detected, and result in an error.

That's simply not possible.

The major reason that the concept of "undefined behavior" exists in
the C language is that many errors cannot be detected, and many other
errors cannot be detected portably or without considerable runtime
overhead.

Consider something as simple as this:

void foo(int *ptr)
{
*ptr = 42;
}

If ptr is a valid pointer-to-int, this is ok. If it's not (either
because it's a null pointer or because it's invalid for any of a
number of other reasons), it invokes undefined behavior. If the
implementation were required to explicitly detect this error, it would
have to have a way of determining whether a pointer is valid; there's
no good way to do that.

I think most languages have instances of this kind of thing. The
better-defined ones enumerate what they are. C is somewhat unusual in
the number of things that invoke undefined behavior (some other
languages either require more run-time checking or disallow some of
the dangerous things, like pointer arithmetic, that C allows), but
it's difficult, if not impossible, to eliminate them entirely without
severely limiting the power of the language.
 
K

Keith Thompson

Wrong. It will put a (unwritable!) string constant somewhere in memory,
and then make p point to it. The string constant being unwritable is
significant - you've been writing through that pointer in this thread,
and you cannot reliably do so if p points to a string constant.

To clarify: Writing to the memory allocated for a string literal
invokes undefined behavior. It may happen to "work" on some
implementations. (It's defined that way for historical reasons.)
 
R

Richard Bos

tinybyte said:
I don't have any difficulties. I am aware of it since the beginning.

All the same, you seem to have difficulties accepting its implications.
If the code is broken, why the compiler didn't told it to me?

Imprimis, because the code is broken in a way that cannot always be
detected. It can simply be detected in this case, but there are
fundamental computational reasons why it isn't always possible whether a
certain pointer points at a const object or not. That's why this is
undefined behaviour, and not a constraint violation.

Secundis, because even if the compiler _had_ normally been able to
detect this instance and would normally have warned you, you have
explicitly told it to shut its mouth and get on with the compilation.
The cast effectively says "trust me, it may look like I'm being an
idiot, but I know what I'm doing". That this is, in this case, not quite
true is not something the compiler can detect.

Richard
 
R

Richard Bos

tinybyte said:
The compiler is allowed to assume that the value it's not changed, but
sometimes it is changed, sometimes not, depends on optimization.
This is inconsistent behavior for me, and in C programming this should
not be ALLOWED. That is all I'm talking about.

It has always been a prime advantage of C that its creators and Standard
Committee have considered efficient behaviour of correct code more
important than consistent behaviour of incorrect code, and I, for one,
am glad that this is the case - it is one of C's properties that endears
it to me.

Richard
 
T

tinybyte

That's a noble goal. You'll see, if you stick around (which I hope
you will), that many of the c.l.c "regulars" have anecdotes they've

I'll stick around here, it's a valuable resource a C (wannabe?) programmer
simply can't miss.
But comp.lang.c is not the place to learn them. In c.l.c people

This is the real thing I missed. This is not the place :)
But once you *have* expanded your knowledge base, please don't
hesitate to use that knowledge wisely in this newsgroup or anywhere
else.

Be sure I will!
That would be incredibly annoying, taken as a real goal. I don't [snip]
and I don't think you really want that, either. (Possible signed
integer overflow in the expression 'a+1', just FYI.)

Thanks for the info, and yes, you're right, I don't want such a thing.
not. That's a result of the Halting Problem, which you can Google
for, if you're interested in the theory behind all this.

Thanks for the hint. I didn't know of this decision problem. And yes,
I am interested in the theory behind, since it helps a lot in avoiding
dead ends :) I was into one...
Final note: Lest my tone sound a bit *too* fatalistic, remember
that you can always ask here as to "Why is such-and-such undefined
behavior?" and you'll be sure to get plenty of interesting answers.

I already got many, but I'm sure I can get even more (maybe on other
topics :)
I hope you'll keep up the learning.

Sure I'll be!

Many thanks Arthur.
Daniele
 
G

glen herrmannsfeldt

Kevin Goodsell wrote:

(snip)
My compiler did. But this is not required, and it is not always possible
or desirable for a compiler to complain about possible undefined behavior.

In the unix tradition there was lint, which gave all the messages
that one might possibly want to see.


DESCRIPTION
lint attempts to detect features of the named C program
files that are likely to be bugs, to be non-portable, or
to be wasteful. It also performs stricter type checking
then does the C compiler. (snip)

Among the possible problems that are currently noted are
unreachable statements, loops not entered at the top,
variables declared and not used, and logical expressions
with constant values. Function calls are checked for
inconsistencies, such as calls to functions that return
values in some places and not in others, functions called
with varying numbers of arguments, function calls that pass
arguments of a type other than the type the function expects
to receive, functions whose values are not used, and calls to
functions not returning values that use the non-existent
return value of the function.

(snip)

Some of those functions have been moved into compilers, but
maybe not all. Many messages are just too tiring to see at
each compilation, but it is nice to be able to check for them
once in a while.

Also, I believe that when given multiple files to check, lint does
more checks between files than would be done by compilers.

-- glen
 
E

E. Robert Tisdale

user said:
Hello,

Here is the program:
> cat main.c
#include <stdio.h>

int main(int argc, char* argv[]) {
const int num = 100;
int *ip = (int*)&num;
*ip = 200;
printf("value of num is %d(%d) \n", num, *ip);
return 0;
}
> gcc -Wall -std=c99 -pedantic -O2 -o main main.c
> ./main
value of num is 200(200)
 
T

Tom St Denis

tinybyte said:
You can get a much improved version of lint for you favorite unix flavor
here:

http://www.splint.org

It works damn well :)

It's available for Cygwin as well [e.g. windows users].

I'd suggest using some caution with splint. It does catch many legitimate
bugs, possible bugs and general "bad coding styles" but it also does report
many things that are not invalid code. E.g.

void func(int *data)
{
if (data == NULL) { trap(); return; }
data[0] = 1;
}

Will report possible dereferencing of a NULL value [etc..]

Overall I agree with the suggestion.

Tom
 
K

Kevin Goodsell

Tom said:
You can get a much improved version of lint for you favorite unix flavor
here:

http://www.splint.org

It works damn well :)


It's available for Cygwin as well [e.g. windows users].

You don't even need Cygwin. There are native Windows builds. But it is
one of the packages you can choose to install with Cygwin, so this is an
easy way to get it (complete with documentation, I assume).
I'd suggest using some caution with splint. It does catch many legitimate
bugs, possible bugs and general "bad coding styles" but it also does report
many things that are not invalid code. E.g.

void func(int *data)
{
if (data == NULL) { trap(); return; }
data[0] = 1;
}

Will report possible dereferencing of a NULL value [etc..]

Overall I agree with the suggestion.

It can definitely be overly paranoid (and sometimes blatantly stupid),
to the point where you get so much output that it's very difficult to
determine what is and is not cause for concern. But I think you can also
enable and disable specific warnings, so with some effort you should be
able to come up with a reasonable set. This could be particularly nice
if your compiler's warnings leave something to be desired (as they often
do).

-Kevin
 
D

Dik T. Winter

> Kevin Goodsell wrote:
>
> (snip)
>
>
> In the unix tradition there was lint, which gave all the messages
> that one might possibly want to see.

How wrong you are. Many cases of undefined behaviour are not detectable.
On the other hand, getting a program "lint free" could be a considerable
effort; even for programs that were completely portable.
 

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
474,129
Messages
2,570,770
Members
47,329
Latest member
FidelRauch

Latest Threads

Top