what is the output of this program?

K

Kenny O'Clock

This came up in a job interview, what is the output of the program
below? I tried to compile and run it myself, but my compiler (lcc-win32)
aborts with this errors....

Warning test2.c: 3 old-style function definition for 'main'
Warning test2.c: 3 missing prototype for 'main'
Warning test2.c: 3 '
Error test2.c: 3 compiler error in d:\lcc\mc71\types.c--assertion failure at line 868
Error c:\test\test2.c 3 Compiler error (trap). Stopping compilation
1 error

Here is the program....

1: #include <stdio.h>
2: void main()
3: {
4: int C = 0;
5: printf("C %s C++\n", C == C++ ? "==" : "!=");
6: }

I don't even have a d:\lcc\mc71\ folder on my computer!
Please help, I don't know what to do anymore!!!
 
W

Walter Roberson

Kenny O'Clock said:
This came up in a job interview, what is the output of the program
below?
1: #include <stdio.h>
2: void main()
3: {
4: int C = 0;
5: printf("C %s C++\n", C == C++ ? "==" : "!=");
6: }

In the expression C == C++,
the variable being incremented is accessed more than once between
sequence points ("except to determine the value to be stored").
That is not permitted, so the result could be anything.
-Usually- the result would be either "C == C++" or "C != C++"
printed out (dependant on the compiler), but the program
could segfault... or, like you found, the -compiler- could fault.
 
W

Walter Roberson

Kenny O'Clock wrote:
In addition to what Walter said, I would like to point you to question
3.2 of the c.l.c FAQ, at http://c-faq.com/expr/evalorder2.html.

Though the impact of that question is a bit reduced because there -is-
a sequence point after the evaluation of C == C++ . That confines
the section of undefined behaviour to be relatively small compared
to typical expressions we see that mix variable accesses with
++ or -- .
 
D

dj3vande

Though the impact of that question is a bit reduced because there -is-
a sequence point after the evaluation of C == C++ . That confines
the section of undefined behaviour to be relatively small compared
to typical expressions we see that mix variable accesses with
++ or -- .

Relatively small, perhaps, but still large enough to include all of the
accesses to C in that line of code.


dave
 
J

Joachim Schmitz

Richard said:
Walter Roberson said:


Firstly, because the Standard imposes no limits on the implementation
with regard to undefined behaviour, the wayward effect of C == C++ is
not limited to the "section" of code within which it occurs.

Secondly (and I only mention it because nobody else has in the five
replies that I've seen), the program exhibits undefined behaviour in
another way, too, because the return type of main is incorrect.
And win-lcc properly warns about this...

Bye, Jojo
 
D

Dan

I don't even have a d:\lcc\mc71\ folder on my computer!
Please help, I don't know what to do anymore!!!

I give questions like that just to seperate who is anal and who isn't (and
don't employ the anal ones - they are most likly to take 10 times as long to
write the same page code that no one is ever going to read again).
And just incase you really are dumb, C is not equal to C++.
 
R

Richard

Dan said:
I give questions like that just to seperate who is anal and who isn't (and
don't employ the anal ones - they are most likly to take 10 times as long to
write the same page code that no one is ever going to read again).
And just incase you really are dumb, C is not equal to C++.

What do you mean C is not equal to C++ ?
 
M

Martin

This came up in a job interview, what is the output of the program
below?
[...]
1: #include <stdio.h>
2: void main()
3: {
4: int C = 0;
5: printf("C %s C++\n", C == C++ ? "==" : "!=");
6: }

I'm sure from the responses you've had you now realise that the program is
an example of how not to write C. I would send the code back to the
interviewers, annotated in red with the problems and explain to them why
it is bad. Maybe (and it's a big maybe) the author *knew* what a load of
tripe the code is and that was part of the test.

Several years ago at interview I was shown some (production) C code and I
pointed out that the use of memcpy for overlapping buffers was undefined..
The interviewer was convinced that memcpy was safe to use it and that
memmove was the unsafe one. Anyway, after the interview, I wrote them a
letter declining a second interview and provided them an extract from the
C Standard where it says memmove must work properly even when its operands
overlap.
 
J

Joachim Schmitz

Richard said:
Joachim Schmitz said:


Implementations are not obliged to diagnose incorrect signatures for
main. If they choose to do so, however, it would be good if they
could come up with some decent diagnostic text. The text given is:
"old-style function definition for 'main'". In fact, there's nothing
"old-style" about the function definition. The problem lies with the
return type, and void main isn't old-style - it's simply *wrong*.
The empty parens are old style, vs. main(void).
 
R

Richard Bos

The empty parens are old style, vs. main(void).

No, they're not. void wasn't in K&R1 at all; so void main() is an _ISO_
declaration, of a function returning nothing (which is wrong for main())
and taking an unspecified number of arguments. If it had been an
old-style declaration, there would have been no void on either side; it
would have been either

main()
{ ....

(which, due to the default int, is correct, although bad style, for a
main() which takes no command line arguments) or

main()
int argc;
char **argv;
{ ....

Neither of those should, of course, be used today, except when faced
with an overwhelming necessity of porting to outdated systems.

Richard
 
C

christian.bau

You should have told the interviewer that you need a valid program in order
to make a proper analysis. Then turn the tables and ask him if he sees
anything wrong with the program as-is...

Not at all. The analysis is quite simple: "void main ()" should be
changed to "int main ()", possibly better to "int main (void)" to make
clear you know that the function has no arguments and that you haven't
just forgotten about them. "C == C++" invokes undefined behavior
because C is both modified and accessed without an intervening
sequence point, so it is impossible to predict what will happen.
Likely outcomes are printing "C == C++" or "C != C++", but anything
else, including crashing, is also possible and entirely the fault of
the programmer who wrote the code.

Nothing wrong with this as an interview question.
 
C

christian.bau

Several years ago at interview I was shown some (production) C code and I
pointed out that the use of memcpy for overlapping buffers was undefined.
The interviewer was convinced that memcpy was safe to use it and that
memmove was the unsafe one. Anyway, after the interview, I wrote them a
letter declining a second interview and provided them an extract from the
C Standard where it says memmove must work properly even when its operands
overlap.

In many implementations, memcpy (dst, dst + n, count) will behave
exactly like memmove (dst, dst + n, count) if n > 0, while memcpy (dst
+ n, dst, count) will usually invoke some rather bizarre behaviour.
That doesn't change the fact that it is undefined behavior, and
relying on this is of course utterly stupid.

For example, a highly optimised version of memcpy intended for a
processor with vector registers could look like this:

size_t i;

if (size % 16 != 0)
{
size_t originalsize = size;
size_t i;
size -= size % 16;
for (i = size; i < originalsize; ++i) dst = src ;
}

for (i = 0; i < size; i += 16)
"Copy 16 bytes from src + i to dst + i";
 
M

Martin

Nothing wrong with this as an interview question.

True, but I would be interested to know if the interviewer explained to
Kenny the expected result, and used the term 'undefined behaviour' when
doing so.
 
F

Flash Gordon

Dan wrote, On 29/05/08 09:14:
The code in question was:
1: #include <stdio.h>
2: void main()
3: {
4: int C = 0;
5: printf("C %s C++\n", C == C++ ? "==" : "!=");
6: }
I give questions like that just to seperate who is anal and who isn't (and
don't employ the anal ones - they are most likly to take 10 times as long to
write the same page code that no one is ever going to read again).

Code never gets reviewed at your company?
And just incase you really are dumb, C is not equal to C++.

The languages in question may be different, but on my system here is
prints "C == C++".

Amusingly, if I change it to ++C we still get:
markg@brenda:~$ cat t.c
#include <stdio.h>
void main()
{
int C = 0;
printf("C %s C++\n", C == ++C ? "==" : "!=");
}
markg@brenda:~$ gcc t.c
t.c: In function ‘main’:
t.c:3: warning: return type of ‘main’ is not ‘int’
markg@brenda:~$ ./a.out
C == C++
markg@brenda:~$

Personally I would not want to work for a company where code like that
would be accepted. The "void main()" I might put up with, but the clear
undefined behaviour for no good reason I would not. I've seen too many
instances of C code which needlessly invokes undefined behaviour
actually causing programs to behave incorrectly. I've fixed a lot of
bugs by gradually increasing the compiler warning level and fixing what
it throws up, especially the undefined behaviour.
 
T

Three Headed Monkey

Joachim Schmitz said:


If that's truly what the message means, it's silly - the compiler is
ignoring a genuine error and instead flagging up perfectly harmless code.

You are ignoring third warning. If I add prototype for main like below,
compiler only issues one warning

#include <stdio.h>
void main(int argc, char **argv)
{
int C = 0;
printf("C %s C++\n", C == C++ ? "==" : "!=");
}

$ make foo9.exe
lc -ansic -pedantic -A -shadows -unused -O -c foo9.c -o foo9.obj
Warning foo9.c: 3 '
Error foo9.c: 3 compiler error in d:\lcc\mc71\types.c--assertion failure at line 868
Error c:\tmp\clc\foo9.c 3 Compiler error (trap). Stopping compilation
1 error
make: *** [foo9.obj] Error 1

Perhaps there is bug.
 
U

user923005

Joachim Schmitz said:
If that's truly what the message means, it's silly - the compiler is
ignoring a genuine error and instead flagging up perfectly harmless code.

You are ignoring third warning. If I add prototype for main like below,
compiler only issues one warning

  #include <stdio.h>
  void main(int argc, char **argv)
  {
    int C = 0;
    printf("C %s C++\n", C == C++ ? "==" : "!=");
  }

$ make foo9.exe
lc -ansic -pedantic -A -shadows -unused -O -c foo9.c -o foo9.obj
Warning foo9.c: 3  '
Error foo9.c: 3  compiler error in d:\lcc\mc71\types.c--assertion failure at line 868
Error c:\tmp\clc\foo9.c 3 Compiler error (trap). Stopping compilation
1 error
make: *** [foo9.obj] Error 1

Perhaps there is bug.

Refusing to compile code that exhibits undefined behavior seems more
like a feature than a bug to me.
In fact, I wish that all my compilers would do that. (At least in the
cases where undefined behavior can be repaired).
 
S

Sandpaper Supplier for Pinocchio

Joachim Schmitz said:




If that's truly what the message means, it's silly - the compiler is
ignoring a genuine error and instead flagging up perfectly harmless code.

You are ignoring third warning. If I add prototype for main like below,
compiler only issues one warning

#include <stdio.h>
void main(int argc, char **argv)
{
int C = 0;
printf("C %s C++\n", C == C++ ? "==" : "!=");
}

$ make foo9.exe
lc -ansic -pedantic -A -shadows -unused -O -c foo9.c -o foo9.obj
Warning foo9.c: 3 '
Error foo9.c: 3 compiler error in d:\lcc\mc71\types.c--assertion failure at line 868
Error c:\tmp\clc\foo9.c 3 Compiler error (trap). Stopping compilation
1 error
make: *** [foo9.obj] Error 1

Perhaps there is bug.

I may be misremembering but I think this is a limitation in the
free version and you need to buy a premium commercial license to
be able to use 'void main()' in lcc-win.
 
K

Keith Thompson

Eric Sosman said:
IMHO that's an unfortunate wish. Here, for example,
is the start of a program that exhibits undefined behavior:

#include <unistd.h>
...

Do you really want the compiler to reject it?

As the Rationale puts it, "the ability to write machine-
specific code is one of the strengths of C." If C were
*unable* to poke I/O registers, *unable* to call dlsym() and
convert a void* to a function pointer, *unable* to overflow
an `int' without trapping, C would be far less useful than
it actually is.

Sure, but there's undefined behavior and then there's undefined
behavior.

There are plenty of constructs whose behavior the standard doesn't
define that can nevertheless be useful in non-portable code.

On the other hand, some constructs, such as ``C == C++'' or
``i = i++'' are, in a sense, more purely undefined, to the extent that
they should never appear in normal C code. Regardless of the
portability of the program, there's always a better way to expression
whatever the intent was.

For such constructs, I certainly have no problem with a compiler
warning about them, or even rejecting the translation unit *if* it can
prove that the code will always be executed.

The tricky (and perhaps undecidable) part is determining which
instances of UB are potentially ok and which are not.

(On the other hand, the way in which the particular compiler
terminated, with an internal assertion failure, indicates a bug in
that compiler -- though the standard doesn't say what a diagnostic has
to look like.)

[...]
 
K

Keith Thompson

Richard Heathfield said:
Three Headed Monkey said: [...]
Error foo9.c: 3 compiler error in d:\lcc\mc71\types.c--assertion failure
at line 868 Error c:\tmp\clc\foo9.c 3 Compiler error (trap). Stopping
compilation 1 error
make: *** [foo9.obj] Error 1

Perhaps there is bug.

Perhaps there is, but you haven't shown this. The cited program
exhibits undefined behaviour, so the implementation is allowed to do
anything it likes with it - and since I don't see any syntax errors
or constraint violations, I see no requirement for any message
whatsoever. For the compiler to have a bug that is relevant to the
above code, it would surely have to be exhibiting (or harbouring)
some inappropriate behaviour when given that code. But since the
Standard allows all behaviours for such code, it is hard to see how
it could be used to demonstrate a bug in the implementation.

I think you're using a far too narrow definition of "bug".

The way I use the term, a failure to conform to the standard (on the
part of an implementation that claims to conform to the standard) is
certainly a bug, but many other things are bugs as well. Responding
to undefined behavior by blowing up rather than by printing a proper
error message, though it doesn't violate the standard, is, in my
opinion, a bug.

Also, I suspect (though I have no direct evidence of this) that the
compiler would react the same way to the same code in a function
that's never called. In such a case, the standard doesn't permit the
compiler to reject the translation unit.

(BTW, I'm not trying to pick on lcc-win here.)
 
U

user923005

     IMHO that's an unfortunate wish.  Here, for example,
is the start of a program that exhibits undefined behavior:

        #include <unistd.h>
        ...

Do you really want the compiler to reject it?

Can the compiler do anything about it?

In the case of:

i = i++;

It can.

In the second case, it can't.
     As the Rationale puts it, "the ability to write machine-
specific code is one of the strengths of C."  If C were
*unable* to poke I/O registers, *unable* to call dlsym() and
convert a void* to a function pointer, *unable* to overflow
an `int' without trapping, C would be far less useful than
it actually is.

     There is an unfortunate tendency among novice C programmers
to resort to undefined or implementation-defined behavior when
portable alternatives exist if one is informed enough and
careful enough to use them.  But sometimes C is used to achieve
effects that simply aren't portable, that rely on behavior left
undefined by the C Standard but defined by the platforms of
interest.  Such programs do compile, and should compile, even
though the C Standard does not define their behavior.

I agree that:
1. It is far better to use fully portable code if possible.
2. Almost every real project invokes undefined behavior at some point
3. Most of the time, we do not even recognize that the behavior is
undefined
4. Even when we recognize that the behavior is undefined, we probably
can't do anything about it (e.g. unistd.h or windows.h)

There are also rare cases where we know for sure that:
1. The behavior is undefined
2. The undefined behavior is undesirable

such as:
i = i++; /* There is literally no sensible reason imaginable to do
this. */

In these few cases, I think a very sensible solution is to issue an
error message and refuse to create a binary.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top