Why is this code valid C++?

J

James Aguilar

Someone showed me something today that I didn't understand. This doesn't
seem like it should be valid C++. Specifically, I don't understand how the
commas are accepted after the function 'fprintf'. What effect do they have
in those parenthesis?

I understand how the or is useful and why never to do it, I'm really just
asking about that construction "(fprintf(stderr, "Can't open file.\n"),
exit(0), 1))".

---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main()
{
FILE *fp = (FILE *) (fopen("file","r") ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----

- JFA1
 
E

E. Robert Tisdale

James said:
Someone showed me something today that I didn't understand.
This doesn't seem like it should be valid C++. Specifically,
I don't understand how the commas are accepted after the function 'fprintf'.
What effect do they have in those parenthesis?

I understand how the or is useful and why never to do it.
I'm really just asking about that construction
"(fprintf(stderr, "Can't open file.\n"), exit(0), 1))".

---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE *fp = (FILE *) (fopen("file","r")
|| (fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----

It means the same thing as:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE* fp = fopen("file", "r");
if (NULL == fp) {
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
}


A comma separated list of expressions

(expression1, expression2, . . ., expressionN)

are evaluated in order and has the value of the last expression.
 
R

red floyd

James Aguilar wrote:


#include <stdlib.h>
#include <stdio.h>

int main()
{
FILE *fp = (FILE *) (fopen("file","r") ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

This actually belongs on comp.lang.c, there is no C++ specific code
here, but...

If you look, the entire boolean expression is in parens. So if fopen
succeeds, the || statement short-circuits, returning the file pointer,
which is cast into a FILE* and assigned to fp.

If the fopen fails, it returns NULL (or 0), which causes the second part
of the || to evaluate. It uses the "," operator, which returns the
value of it's RHS. So, it does fprintf [returns int], exit ["returns"
void] and 1 [int]). Note that the expression 1 will never be evaluated,
but the compiler needs it to be happy, since a void cannot be cast to a
FILE*.

The original coder was trying to be clever and "efficient", but I
suspect that the code for this is no more efficient that the much more
readable:

#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* fp = fopen("file", "r");
if (fp == NULL)
{
fprintf(stderr, "Can't open file.\n");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
 
A

Andrey Tarasevich

James said:
Someone showed me something today that I didn't understand. This doesn't
seem like it should be valid C++. Specifically, I don't understand how the
commas are accepted after the function 'fprintf'. What effect do they have
in those parenthesis?

I understand how the or is useful and why never to do it, I'm really just
asking about that construction "(fprintf(stderr, "Can't open file.\n"),
exit(0), 1))".

---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main()
{
FILE *fp = (FILE *) (fopen("file","r") ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----
...

This code is "valid" in a sense that it is "well-formed", i.e. it will
compile. It will compile simply because someone forced it to be
well-formed by introducing extra typecasts into otherwise completely
meaningless code. The code doesn't make any sense. It will not do
anything useful. Are you sure you reproduced it correctly?

I can only guess that the code was meant do look as follows

...
FILE *fp;
(fp = fopen("file","r")) ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));
...

This will "work". In short, if the 'fopen' succeeds, the program will
not try to evaluate the second operand of '||' operator and the whole
thing will be equivalent to 'fp = fopen("file","r")'.

If 'fopen' fails (i.e. returns NULL), the program will proceed to
evaluating the second operand of '||' expression: print the error
message and terminate.

Read more about the properties of built-in '||' operator and comma (',')
operator for more details. I believe, you can find some useful
information in the FAQ.
 
I

Ivan Vecerina

James Aguilar said:
Someone showed me something today that I didn't understand.
This doesn't seem like it should be valid C++. Specifically, I don't
understand how the commas are accepted after the function 'fprintf'. What
effect do they have in those parenthesis?
When not used to separate parameters, within an expression, the comma
acts as a sequence operator: it evaluates the expression on its left,
then the expression on its right, and returns the result of the latter.
For example:
int x = (3,5); // initializes x with '5'
In practice, the use of the comma operator is (&should be) rare in C++.
I understand how the or is useful and why never to do it, I'm really just
asking about that construction "(fprintf(stderr, "Can't open file.\n"),
exit(0), 1))".

Yes, this code looks like someone trying to write Perl in C++ - which
doesn't make sense and fails because of stronger typing and different
operator semantics.
FILE *fp = (FILE *) (fopen("file","r") ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));

In Perl, the || operator returns its left-side expression if it is
non-zero. In C++, it returns a bool, so although the code compiles,
fp will be initialized with (FILE*)1 - which will lead to undefined
behavior if it is used.
Such code would not even have its place in an obfuscated C code contest,
because it doesn't even work, and "leaks" a file handle.

The ",1" is needed so that the result of the ...,exit(0),1 expression
will be an integer, compatible with the || operator.
( exit(0) has a void return type, and would be an error).
 
V

Victor Bazarov

James said:
Someone showed me something today that I didn't understand. This doesn't
seem like it should be valid C++. Specifically, I don't understand how the
commas are accepted after the function 'fprintf'. What effect do they have
in those parenthesis?

'exit(0)' is executed. 1 is just for syntactic and semantic correctness,
since the right operand of || has to have a value and 'exit' doesn't
return any.
I understand how the or is useful and why never to do it, I'm really just
asking about that construction "(fprintf(stderr, "Can't open file.\n"),
exit(0), 1))".

---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main()
{
FILE *fp = (FILE *) (fopen("file","r") ||
(fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----

It's not valid C++. It's valid C90. But it's very close to being valid
C++, though.

The || operator causes the evaluation of the left operand. If the result
of that evaluation is not zero (and in this case if 'fopen' returns a non-
null pointer), the evaluation stops. HOWEVER, 'fp' may simply get a wrong
value. Once the left side of || is non-zero, it _may_ be converted (and
should be converted) to a bool value, which is 'true'. After that the
same 'true' is cast back to (FILE*), which causes undefined behaviour.

If the call to 'fopen' returns NULL, then the right side of || is then
evaluated. The code has a parenthesized expression with three separate
subexpressions connected by two operator comma. There is a sequence point
at every comma, so first 'fprintf' is executed. It supposedly prints out
the error message. Then 'exit(0)' is executed. That forces the end of
the program. The '1' after that is not really needed, but is there to
probably satisfy the need to have a value as the result of evaluating the
parenthesized right-hand side of the || operator.

Since if 'fopen' fails to open the file, the 1 is never cast to FILE* and
'fp' is never assigned that value, there is no concern for undefined
behaviour which should happen otherwise because casting an integer to
a pointer is not defined unless the integer itself what obtained by the
symmetrical cast *from* a pointer before.

So, as you can see, the only problem here is when it actually _can_ open
the file.

V
 
A

Andrey Tarasevich

red said:
This actually belongs on comp.lang.c, there is no C++ specific code
here, but...

If you look, the entire boolean expression is in parens. So if fopen
succeeds, the || statement short-circuits, returning the file pointer,
which is cast into a FILE* and assigned to fp.

No, even if it short-circuits, it will not return file pointer. Boolean
expressions (as this one) return only 'true' or 'false', nothing else.
When 'fopen' succeeds and this '||' expression short-circuits, it
returns 'true' which is then cast to 'FILE*' type and stored in 'fp'.
Casting 'true' to 'FILE*' type doesn't make any sense.

This obfuscated technique can be applied properly (see my another
message), but the above is not it.
 
A

Andrey Tarasevich

E. Robert Tisdale said:
---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE *fp = (FILE *) (fopen("file","r")
|| (fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----

It means the same thing as:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE* fp = fopen("file", "r");
if (NULL == fp) {
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
}
...

No, it doesn't. It means the same thing as

FILE* fp;
if (fopen("file", "r") != NULL)
fp = (FILE*) true;
else
{
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}

Do you notice the difference?
 
E

E. Robert Tisdale

Andrey said:
E. Robert Tisdale said:
James said:
---- CODE ----

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE *fp = (FILE *) (fopen("file","r")
|| (fprintf(stderr, "Can't open file.\n"), exit(0), 1));
return 0;
}

---- /CODE ----

It means the same thing as:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
FILE* fp = fopen("file", "r");
if (NULL == fp) {
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
}

No, it doesn't. It means the same thing as

FILE* fp;
if (fopen("file", "r") != NULL)
fp = (FILE*) true;
else
{
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}

Do you notice the difference?

It depends upon whether you think that
it is a C program or a C++ program.

I think that James Aguilar posted a C program
to the comp.lang.c++ newsgroup [by mistake].
 
M

MatrixV

In fact, printf and scanf are implemented as functions. What you see is the
standard c++ function style. But I still don't understand why some people
use it.
 
J

James Aguilar

E. Robert Tisdale said:
It depends upon whether you think that
it is a C program or a C++ program.

I think that James Aguilar posted a C program
to the comp.lang.c++ newsgroup [by mistake].

Could be. I thought that C-language was a subset of C++-language, but I
heard there were some differences. Is this one?

-JFA1
 
V

Victor Bazarov

James said:
[...] I thought that C-language was a subset of C++-language, but I
heard there were some differences. Is this one?

You thought incorrectly. You heard correctly. Yes, this is one.
C is still handling || as if both operands are of "scalar" type and
simply compares them to 0. In C++ both operands are converted to type
'bool' (although the second is not really evaluated or converted if
the first one evaluates to true).

V
 
A

Andrey Tarasevich

E. Robert Tisdale said:
No, it doesn't. It means the same thing as

FILE* fp;
if (fopen("file", "r") != NULL)
fp = (FILE*) true;
else
{
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}

Do you notice the difference?

It depends upon whether you think that
it is a C program or a C++ program.

I think that James Aguilar posted a C program
to the comp.lang.c++ newsgroup [by mistake].

It doesn't make any difference. In C language logical operators evaluate
to either integral 0 or 1. In C it would be equivalent to

FILE* fp;
if (fopen("file", "r") != NULL)
fp = (FILE*) 1;
else
{
fprintf(stderr, "Can't open file.\n");
exit(EXIT_SUCCESS);
}

Converting integral '1' to 'FILE*' type doesn't make any more sense than
converting 'true' to the same type. The code is meaningless in both C
and C++ in virtually the same way.
 
D

Dave Vandervies

E. Robert Tisdale said:
It depends upon whether you think that
it is a C program or a C++ program.

I think that James Aguilar posted a C program
to the comp.lang.c++ newsgroup [by mistake].

Could be. I thought that C-language was a subset of C++-language, but I
heard there were some differences. Is this one?

There are constructs that are valid C and not valid C++, and constructs
that are valid in both languages but have different meanings, and these
constructs tend to show up in most nontrivial C code, so C is not a
subset of C++.

The code you posted was, as far as I saw (I didn't look too closely),
in the common subset of the two languages (that is, it was valid-ish
and had the same meaning in both), though.


dave
(int *old=malloc(sizeof *old),*new=malloc(sizeof *new);)
 
V

Victor Bazarov

Victor said:
[..]
It's not valid C++. It's valid C90. [...]

My bad. It's not valid C90 either. I trust Andrey's
memory, I don't have a copy of C90 Standard any more.
 
A

Andrey Tarasevich

Victor said:
[...] I thought that C-language was a subset of C++-language, but I
heard there were some differences. Is this one?

You thought incorrectly. You heard correctly. Yes, this is one.
C is still handling || as if both operands are of "scalar" type and
simply compares them to 0. In C++ both operands are converted to type
'bool' (although the second is not really evaluated or converted if
the first one evaluates to true).

This doesn't mean though that '||' in C will preserve the
non-zero-operand and return its value as the result. In C '||' always
returns '0' or '1', which corresponds to 'true' or 'false' in C++. For
the original piece of code it is not a critical difference. It is as
broken in C, as it is in C++.
 
D

Dave Vandervies

Victor Bazarov said:
C is still handling || as if both operands are of "scalar" type and
simply compares them to 0. In C++ both operands are converted to type
'bool' (although the second is not really evaluated or converted if
the first one evaluates to true).

Are there any cases where comparison to 0 and conversion to bool will
give different results? It seems to me that the C++ definitions of
conversions to and from bool don't allow the result to ever be different
from the C definition of compare-to-0.


dave
 
V

Victor Bazarov

Dave said:
Are there any cases where comparison to 0 and conversion to bool will
give different results? It seems to me that the C++ definitions of
conversions to and from bool don't allow the result to ever be different
from the C definition of compare-to-0.

There probably isn't, as far as "scalar" types are concerned, and since C
doesn't have overloaded operators (at least yet), there is no fear about
'conversion to bool' conflicting with 'comparison to 0', which in C++
can easily give different results but only for UDTs.

V
 
J

Jack Klein

'exit(0)' is executed. 1 is just for syntactic and semantic correctness,
since the right operand of || has to have a value and 'exit' doesn't
return any.


It's not valid C++. It's valid C90. But it's very close to being valid
C++, though.

The || operator causes the evaluation of the left operand. If the result
of that evaluation is not zero (and in this case if 'fopen' returns a non-
null pointer), the evaluation stops. HOWEVER, 'fp' may simply get a wrong
value. Once the left side of || is non-zero, it _may_ be converted (and
should be converted) to a bool value, which is 'true'. After that the
same 'true' is cast back to (FILE*), which causes undefined behaviour.

No, it does not, this conversion is well defined.

3.9.1 Fundamental types P7: "Types bool, char, wchar_t, and the signed
and unsigned integer types are collectively called integral types."

5.2.10 Reinterpret cast P5: "A value of integral type or enumeration
type can be explicitly converted to a pointer."

And 5.4 Explicit type conversion (cast notation) makes it quite clear
that the C-style case can perform any valid conversions that any C++
cast can.

So the explicit cast of the value 1 or true is valid in either C or
C++, regardless of whether that value has the type bool (C++), int
(C90), or _bool (C99).

The C++ standard in 5.2.10 P5 includes the clause "mappings between
pointers and integers are otherwise implementation-defined.", while I
prefer the C standard's stronger disclaimer:

========
An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be
correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.
========

In any case, the conversion to pointer is not undefined, but any
attempt to dereference the resulting pointer most certainly is.
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top