Plz explain me the following code

K

Keith Thompson

Kenneth Brody said:
Note that I have seen compilers issue warnings on the "=-" pair. That
used to be a valid alternative to "-=", and the warning stated that it
was not using that version.
[...]

I've seem a compiler issue a warning on "=-" and then go ahead and
interpret as equivalent to "-=", even though it's been unambiguously
"=" followed by "-" at least since K&R1. The compiler (VAXC)
was in production use about 10-11 years ago.
 
K

Keith Thompson

Kenneth Brody said:
Keith Thompson wrote:
[...]
If fclose() fails on an input file, there's probably not much you
can sensibly do in response; I'm not even sure why fclose() on an
input file might fail

Perhaps the file was on a network drive, and some network error
occurred? ("I told the other computer to close the file, but it never
responded to the request, so I have no idea what the other computer
has done, if anything.")
(unless, say, you've already closed the file).

Well, that would be UB, but it's certainly not unreasonable for an
implementation to trap the fclose-on-a-closed-stream condition and
return failure.

That might not be practical. fclose() takes a FILE* argument; it
could easily do the equivalent of free() on its argument before
returning.

I suppose the implementation could compare the argument to a list of
known open files (potentially UB but ok for the implementation), but
there's always the risk of the same chunk of memory being re-used:

f1 = fopen(...); /* mallocs memory for the FILE object */
fclose(f1); /* frees the FILE object */
f2 = fopen(...); /* mallocs the same memory for the FILE object */
fclose(f1); /* UB; in this case it's equivalent to fclose(f2); */
 
S

Seebs

Of course, then you throw in

i = ++i * ++i;

and you have even more possible outcomes.

And then you have pointers, where detecting such conflicts becomes
impossible at compile time:

extern int i;
extern int *pt; /* where pt == &i */
i = (*pt)++;

Yup.

At which point, it might make sense to go for a middle ground of "we really
can't tell you what order these things happen in, but they will happen in
SOME order."

In practice, you can't *use* them for anything, but it's no longer a risk that
things will explode spectacularly, just that you'll get a useless result.

The alternative is to commit to a possibly-very-inefficient ordering -- and
the nature of the beast is that there is no way to decide which ordering
is the most efficient and will stay the most efficient across multiple
targets, etcetera.

It's a tradeoff. There's a lot of room for languages which say "if you want
to specify an order, do, and if you don't, the compiler will do what it thinks
is best."

-s
 
O

ozbear

I am not convinced of this.


Yes, it is. And I am not sure they are making the right choice. There
is a very definite tradeoff there.

In C, if I want to ensure that operations occur in a specific order, or
at least appear to, I can. If I don't care what order they occur in,
though, I can also express that.

In a language with more rigidly defined semantics, there may be no way
for me to express "these three things need to happen, but I don't care
in what order."

If you don't care in what order they happen, why would you need to
express that you don't?

Oz
 
P

Phil Carmody

If you don't care in what order they happen, why would you need to
express that you don't?

Straw man. Who says that you *need* to express that you don't?
Keith, and I, and many others no doubt, simply for the ability
to express that we don't some of the times when we don't. Often
this is for the sake of brevity, and to keep logically-related
actions inseperable, but isn't limited to those 2 classes.
(A common example would be *dest++=*src++, and its ilk.)

Phil
 
S

Seebs

If you don't care in what order they happen, why would you need to
express that you don't?

To tell the system to do whatever's fastest, not whatever exactly preserves
a trait I don't care about.

Imagine, if you will, that careful analysis of cache fills shows us that
evaluating a particular expression right-to-left instead of left-to-right
trims about 5-10% off its execution time.

In C, fine, the compiler can do that. It's allowed; there is no specified
order of evaluation. If I really want to force left-to-right, I can at the
very least hint quite strongly by breaking the expression into multiple
expressions.

In a language with carefully, rigidly, defined ordering, I *can't*.

And since it might be that whether left-to-right or right-to-left is faster
depends on other circumstances, I can't even just write the one that's
better. I just have to accept that the language will waste a ton of execution
time carefully preserving "semantics" which were in fact purely accidental,
and which I had no intention of specifying.

The option of not specifying a thing you don't care about is important for
some applications.

-s
 
F

Flash Gordon

Seebs said:
To tell the system to do whatever's fastest, not whatever exactly preserves
a trait I don't care about.

Imagine, if you will, that careful analysis of cache fills shows us that
evaluating a particular expression right-to-left instead of left-to-right
trims about 5-10% off its execution time.

In C, fine, the compiler can do that. It's allowed; there is no specified
order of evaluation. If I really want to force left-to-right, I can at the
very least hint quite strongly by breaking the expression into multiple
expressions.

In a language with carefully, rigidly, defined ordering, I *can't*.

And since it might be that whether left-to-right or right-to-left is faster
depends on other circumstances, I can't even just write the one that's
better. I just have to accept that the language will waste a ton of execution
time carefully preserving "semantics" which were in fact purely accidental,
and which I had no intention of specifying.

If you have a processor which can do multiple things in parallel it can
be even more interesting. If the compiler knows you don't care what
order things are done in it can produce code which allows the processor
to do them in parallel. If it has to preserve the order then it has to
make sure the processor at least appears to preserve the order slowing
things down.
The option of not specifying a thing you don't care about is important for
some applications.

Agreed. At times I would actually like *more* freedom to express that I
don't care about the order.
 
N

Nick Keighley

Processor-Dev1l wrote:
Main reason why I thought [i=i++] can work is I spent really a lot
of times on HLL languages (like java, c#), where such constructions
are not undefined and they have their rules.
i = i++ + ++j;
but be translated by managed code as
++j;
i = i + j;
i++;

In your example, why does j's increment take place before the assignment,
but i's takes place after?

I have never touched C#, and I don't know Java well enough to comment on
this particular construct.  But, if you were to ask here, most would agree
that requiring that interpretation puts an unnecessary burden on the
compiler, and can prevent it from optimizing the code.

What do C# and Java say about:

     i = j++ + ++i;

I was curious so I went digging in the java spec. I couldn't actually
find a mention of this (but this is probably more a reflection of how
carefully I searched).

java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7

possibly a combination of "Evaluate Left-Hand Operand First" and
"Evaluate
Operands before Operation" leads to this behaviour
 From your comment above, I will assume that they require it be equivalent to:

     ++i;
     i = i + j;
     j++;

Must i actually be incremented prior to performing the addition, or is the
compiler allowed to optimize it to be:

     i += j + 1;
     j++;

<snip>
 
J

James Kuyper

Kenneth Brody wrote:
....
Note that I have seen compilers issue warnings on the "=-" pair. That
used to be a valid alternative to "-=", and the warning stated that it
was not using that version.

I just fixed a bug in one of my programs, of exactly that form, that was
put into it about a dozen years ago. The program passed numerical tests
because the assignment expression was executed only when the left
operand was already almost equal to 0, within our accuracy requirements.
An exact comparison with an authoritative source would have shown the
discrepancy, but there is no such source.
 
T

Tom St Denis

Tom said:
Tom St Denis wrote:
[...]
The problem really is that there a lot of people with arbitrary levels
of training and experience who call themselves developers.  To be a
professional developer you basically have to show some modicum of
understanding of the syntax.  I've seen quite a few developers who do
any number of classic development mistakes ...
- putting more than one exported function in a .c file
Guilty.
The point behind this one is it complicates smart linking [you get
more code than you may need in the image], it makes building take
longer, etc...  It also makes it harder to work in a team since you
now have more resources locked up behind a cvs checkout.

Ah! I was wondering about that one. The only code that I myself have
designed which did that involved multiple functions that shared file
scope data with internal linkage. The way they were designed, it would
be almost completely pointless to write code that didn't call all of
the relevant functions (for instance, one function writes to the
shared date, the other reads it).

The reasons you give wouldn't apply to such code.

There are going to be exceptions to most any rule at some point. It
really depends on the scope. But generally it's a bad idea to just
start throwing functions that can logically be separate units in the
same unit.

A lot of the "rules" that I listed are things that usually just happen
by habit. I don't really have to think hard about creating a slew of
new .c files when I'm adding functionality. Or indenting, or
initializing things (lined up, etc...).

One of the arguments I see against such organization is that it's too
much of a thought exercise and gets in the way of actually developing
code. Which of course as we all know is the exact opposite once you
have to revisit some 6+mo old code that you don't quite remember how
it works, etc...

Tom
 
R

Richard Tobin

If you don't care in what order they happen, why would you need to
express that you don't?
[/QUOTE]
To tell the system to do whatever's fastest, not whatever exactly preserves
a trait I don't care about.

One might regard the need to do this as merely a temporary deficiency
in compiler technology. If you don't care what order things will
happen in, it's almost always because it makes no difference to the
final result. And in theory, a Sufficiently Clever Compiler could
determine this at least as well as the programmer.

Even a not-so-clever compiler can often tell that, say, the argument
expressions in a function call have no side effects, and it can
therefore evaluate them in any order even if the standard says
left-to-right.

-- Richard
 
R

Richard Tobin

Keith Thompson said:
If fclose() fails on an input file, there's probably not much you
can sensibly do in response; I'm not even sure why fclose() on an
input file might fail (unless, say, you've already closed the file).

It might get a write error updating the file access time. But as you
say, there's probably not much you can sensibly do about it.

-- Richard
 
S

Seebs

One might regard the need to do this as merely a temporary deficiency
in compiler technology. If you don't care what order things will
happen in, it's almost always because it makes no difference to the
final result. And in theory, a Sufficiently Clever Compiler could
determine this at least as well as the programmer.

Even a not-so-clever compiler can often tell that, say, the argument
expressions in a function call have no side effects, and it can
therefore evaluate them in any order even if the standard says
left-to-right.

Hmm. Good point. Assuming that the specified ordering doesn't actually
mean that things MUST be evaluated in that order, only that they must
not produce detectably different results, that's probably viable.

-s
 
D

Dik T. Winter

> In article <[email protected]>,

>
> One of the goals of modern language design (a class of which C is not a
> member) is to eliminate the concept of undefined behavior.

That may be the design goal, but the question remains: what it is
intended to do!
 
B

Ben Bacarisse

Seebs said:
Hmm. Good point. Assuming that the specified ordering doesn't actually
mean that things MUST be evaluated in that order, only that they must
not produce detectably different results, that's probably viable.

The trouble with relying on the compiler is that it can't tell what
matters and what does not. Currently, if i write:

x = f() + g();

I am saying that I don't care about the order of the calls. If an
order were mandated with permission to reorder when it can't matter
almost any side-effect at all in f or g would prevent re-ordering.
Maybe f and g have logging calls. These are side-effects, but I don't
care what order they occur in -- that is my choice, but the compiler
can't ever know that! Such cases may be rare enough to not matter in
practise, of course.
 
D

Dik T. Winter

> > Processor-Dev1l wrote: ....
> > > Main reason why I thought [i=i++] can work is I spent really a lot
> > > of times on HLL languages (like java, c#), where such constructions
> > > are not undefined and they have their rules. > >
> > > i = i++ + ++j;
> > > but be translated by managed code as
> > > ++j;
> > > i = i + j;
> > > i++;
> >
> > In your example, why does j's increment take place before the assignment,
> > but i's takes place after? ....
> > What do C# and Java say about:
> >
> > i = j++ + ++i;
....
> java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7

I have looked around. According to other parts on that page the increments
in i++, ++j, j++ and i++ are completed before the assignment itself is
performed. So in Java the statement:
i = i++;
is effectively a no-op, and
i = i++ + ++j;
is effectively the same as
i = i + ++j;
 
J

jameskuyper

Kenneth said:
....
Perhaps replacing UB with something like "anything left undefined will, on
hosted implementations, behave in a manner consistent with the host"? So,
dereferencing a NULL pointer on Unix can't launch nuclear missiles, since
Unix says it should SEGV.

Basically, "I am not imposing any restrictions, but I do require that you
follow the restrictions placed on you by the host platform".

If the host platform "places" those restrictions, why does the C
standard need to say anything about it? Isn't the host's statement of
those restrictions sufficient?
Of course, that still doesn't help in my "i = (*pt)++" example above, since
the compiler must still take the non-optimal approach that pt _might_ point
to i, and therefore _can't_ run them in parallel. (Unless, I suppose, the
host says "if you do this, the CPU will lock", rather than "don't do this".)

"Oh," I hear you saying, "you just add something to tell the compiler that
pt will never point to i[*], and now you can optimize it." Fine, but what
if you lie?


[*] - Does "extern int * restrict pt" do this?

Pretty much.
 
K

Keith Thompson

jameskuyper said:
Kenneth Brody wrote: [...]
Perhaps replacing UB with something like "anything left undefined will, on
hosted implementations, behave in a manner consistent with the host"? So,
dereferencing a NULL pointer on Unix can't launch nuclear missiles, since
Unix says it should SEGV.

Basically, "I am not imposing any restrictions, but I do require that you
follow the restrictions placed on you by the host platform".

If the host platform "places" those restrictions, why does the C
standard need to say anything about it? Isn't the host's statement of
those restrictions sufficient?
[...]

Currently, if the host platform specifies that deferencing a null
pointer causes a certain trap that terminates the current program,
a C program that dereferences a null pointer can still behave
arbitrarily badly, because an optimizing compiler is permitted to
assume that no null pointer is ever dereferenced. With Kenneth's
suggestion, this kind of optimization would be prohibited; the
generated code would actually have to dereference the null pointer
and trigger the trap (or behave equivalently, I suppose).

If such a change were made to the language, then (a) optimization
in conforming compilers would become less effective by some unknown
(to me) margin, and (b) existing compilers would have to be modified
to meet the new requirements (with resulting bugs).
 
K

Keith Thompson

Kenneth Brody said:
Keith said:
Kenneth Brody said:
Note that I have seen compilers issue warnings on the "=-" pair. That
used to be a valid alternative to "-=", and the warning stated that it
was not using that version.
[...]

I've seem a compiler issue a warning on "=-" and then go ahead and
interpret as equivalent to "-=", even though it's been unambiguously
"=" followed by "-" at least since K&R1. The compiler (VAXC)
was in production use about 10-11 years ago.

A quick test on a compiler here compiles this "correctly", without a
peep, even at the highest warning level:

extern int i;
void foo(void)
{
i =- 1;
}

(ie: it assigns negative 1 to i.)

The result is consistent with "=-" being interpreted as a compound
assignment operator, unless you assigned some value other than 0
to i before calling foo(). Unless you mean that you examined the
generated code (it's unlikely that a compiler old enough to interpret
"=-" as an assignment would be smart enough to do the dataflow
analysis necessary to optimize the decrement to an assignment).
As I said, my K&R1 disappeared last century, so I can't check what it
said. But, as you also have seen, there were post-K&R1 compilers that
at least knew that it could have been the same as "-=".

These days, you're very unlikely to find a C compiler that doesn't
support prototypes, which were introduced in C89 / K&R2. You're even
less likely to find a C compiler that treats "=-" as a compound
assignment, something that was gone from the language as of K&R1, 11
years earlier. I think VAXC was an anomoly.
 
J

jameskuyper

Kenneth said:
Except that nothing requires that:

char foo(void)
{
return *(char *)NULL;
}

actually dereference a NULL pointer. The compiler is free to generate any
code is wishes, AFAIK, including code to (attempt to) reformat the disk.

Does a compiler that generates such code satisfy the "restrictions
placed upon you by that platform"? If so, there's no problem. If not,
then the platform's restrictions are sufficient reason to reject that
compiler; there's no need to mention them in the C standard, so as to
find a second reason for rejecting it.
 

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,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top