return (i = 1, NULL)

W

William Ahern

is this legal:

FILE *func(void) {
extern int errno;

return (errno = EINVAL, NULL);
}

my compiler (gcc 2.9.x on openbsd) is complaining that i'm returning a
pointer from int w/o a cast. gcc 3.x on linux, however, never made a peep
about it, so possibly 2.9.x is just grumpy.

i suppose the relevent question is, can one group different types like
that?

tia,

Bill
 
J

Jun Woong

William Ahern said:
is this legal:

FILE *func(void) {
extern int errno;

return (errno = EINVAL, NULL);
}

my compiler (gcc 2.9.x on openbsd) is complaining that i'm returning a
pointer from int w/o a cast. gcc 3.x on linux, however, never made a peep
about it, so possibly 2.9.x is just grumpy.

To avoid the discussion on the name space pollution, let me rewrite
your code:

FILE *func(void)
{
extern int my_errno;

return (my_errno = SOME_VALUE, NULL);
}

And the expression given to the return statement is not a constant
expression, becuase it contains the comma operator. Therefore, in the
*full* expression for the return, NULL doesn't qualify as a null
pointer constant. If your implementation #defines NULL simply as 0 (or
similar ones whose types are not assignment-compatible with FILE *),
your program is illegal, which is what gcc complains about.
 
S

Samuel Barber

William Ahern said:
is this legal:

FILE *func(void) {
extern int errno;

return (errno = EINVAL, NULL);
}

my compiler (gcc 2.9.x on openbsd) is complaining that i'm returning a
pointer from int w/o a cast. gcc 3.x on linux, however, never made a peep
about it, so possibly 2.9.x is just grumpy.

Compiler bug. The return statement does not require ( ), incidentally.

Sam
 
C

Chris Spiegel

Compiler bug. The return statement does not require ( ), incidentally.

It's not a compiler bug. On FreeBSD, and thus I assume OpenBSD, NULL is
an unadorned zero:
#define NULL 0

On Linux, it's
#define NULL ((void*)0)

Both are legal definitions of NULL. However, in some circumstances the
differences can bite you. This is one of those.

Since the function is defined as returning a pointer, you should return
a pointer (surprise). The issue here is what, exactly, a null pointer
is.

A plain old zero is allowed to be a null pointer, given the proper
context. One of the constraints is that it must be a constant
expression, and the comma operator is not allowed in a constant
expression.

So, assuming NULL is just zero, we have something like:
return (errno = EINVAL, 0);

The value that's being returned has type integer, and is zero. But
it's not a constant expression, and thus converting it to a pointer type
isn't going to create a null pointer (or rather, it might or might not
depending on your implementation, but it's safer to assume it won't.)

However, if NULL is ((void*)0), then it's:
return (errno = EINVAL, ((void*)0));

The value being returned here has type void*, and is a null pointer. It
is not a constant expression, but that's OK, because a constant
expression is only required to convert an integer zero to a null
pointer, not to convert a null pointer to another pointer type.

This is a very confusing part of C, but it does show why you should
always pretend NULL is just a plain old zero. In fact, some people
refuse to use NULL at all because of issues like this; instead, they
just use a zero as a null pointer. I like NULL, personally, because I
know when to cast it and using NULL just makes the code a bit more self
documenting, IMO.

To be complete, the proper fix for the above code is:
return (errno = EINVAL, (void*)NULL);
or
return (errno = EINVAL, (void*)0);
or
... something that doesn't use the comma operator!

Chris
 
S

Samuel Barber

Chris Spiegel said:
A plain old zero is allowed to be a null pointer, given the proper
context. One of the constraints is that it must be a constant
expression, and the comma operator is not allowed in a constant
expression.

So, assuming NULL is just zero, we have something like:
return (errno = EINVAL, 0);

The value that's being returned has type integer, and is zero. But
it's not a constant expression, and thus converting it to a pointer type
isn't going to create a null pointer (or rather, it might or might not
depending on your implementation, but it's safer to assume it won't.)

However, if NULL is ((void*)0), then it's:
return (errno = EINVAL, ((void*)0));

The value being returned here has type void*, and is a null pointer. It
is not a constant expression, but that's OK, because a constant
expression is only required to convert an integer zero to a null
pointer, not to convert a null pointer to another pointer type.

Thank you for the correction.
To be complete, the proper fix for the above code is:
return (errno = EINVAL, (void*)NULL);
or
return (errno = EINVAL, (void*)0);
or
.. something that doesn't use the comma operator!

The last is by far the best fix, most importantly because (void*)0 is
not portable to C++.

Sam
 
W

William Ahern

Samuel Barber said:
Chris Spiegel <[email protected]> wrote in message news:<[email protected]>...

The last is by far the best fix, most importantly because (void*)0 is
not portable to C++.

Sam

I liked the comma form because of its expressiveness. I've used it
exclusively w/ setting errno in a bunch of code (tho, this was the only
instance where I was returning a pointer rather than an int). In these cases
you really are returning two values, and it makes the code much more
readable w/o the additional braces since I love to do a lot of sanity
checking (since I never know where I might copy+paste the code).

But, I'll be mindful of the pitfalls. Thanks Chris for the great
explanation.

- Bill
 
B

Ben Pfaff

The last is by far the best fix, most importantly because (void*)0 is
not portable to C++.

When writing C code, there is no reason to worry about what C++
compilers will think of it. (Header files are a possible
exception.)
 
K

Kevin D. Quitt

return (my_errno = SOME_VALUE, NULL);


Why not:

my_errno = SOME_VALUE; return NULL;
 
W

William Ahern

Kevin D. Quitt said:
Why not:

my_errno = SOME_VALUE; return NULL;

Because I usually use brevity and clarity like so:

if (check_parameter)
return (errno = EINVAL, NULL);

Tho, this was the first time I had to return a null pointer rather
than another error identifer like -1.

The alterntives are:

if (some_condition) {
errno = EINVAL;
return NULL;
}

[three or four more time]

or

if (some_condition1 || some_condition2 || some_condition3 || ...) {
errno = (some_condition)? ENAMETOOLONG : EINVAL
return NULL;
}

Neither of those are very satisfactory for code that isn't even central
to the purpose of the function.
 
A

Arthur J. O'Dwyer

Because I usually use brevity and clarity like so:

if (check_parameter)
return (errno = EINVAL, NULL);

Tho, this was the first time I had to return a null pointer rather
than another error identifer like -1.

The alternatives are:

if (some_condition) {
errno = EINVAL;
return NULL;
}
or

if (some_condition1 || some_condition2 || some_condition3 || ...) {
errno = (some_condition)? ENAMETOOLONG : EINVAL
return NULL;
}

I think the simplest solution has been determined to be:

if (some_condition)
return (errno = EINVAL, (void *) NULL);

Still, I agree that this is almost certainly obfuscation. Have
you considered my /idiom du jour/,

if (some_condition1)
goto exit_failure1;
if (some_condition2)
goto exit_failure2;
...

exit_failure1:
errno = EINVAL;
return NULL;

exit_failure2:
errno = ENAMETOOLONG;
return NULL;


Very compact, and has the added bonus that all the "exit" code is in
one place. That means it's harder to miss a free(), and easier to
figure out all the possible return codes (which should be documented
anyway, of course, but still..).

HTH,
-Arthur
 
S

Samuel Barber

Ben Pfaff said:
When writing C code, there is no reason to worry about what C++
compilers will think of it. (Header files are a possible
exception.)

C code is not neccessarily bad if it's not C++-compatible, but
C++-compatibility is certainly a valid point when considering the
merits of a C code fragment in the abstract. There are many reasons to
prefer C++ compatibility, all things being equal.

Sam
 
G

Greg Comeau

C code is not neccessarily bad if it's not C++-compatible, but
C++-compatibility is certainly a valid point when considering the
merits of a C code fragment in the abstract. There are many reasons to
prefer C++ compatibility, all things being equal.

I think Sam has a reasonable point. Of course, it need not
always be the case, but clearly many C programmers
do -- and should -- worry about what C++ compilers will think.
 
G

Greg Comeau

Since C translation units can be linked into C++ programs, why can't
they just compile their .c files with a C compiler, even when they're
part of a C++ project?

They can. But to require everybody do so is not always
sufficient, just like not requiring them to do so isn't.
When/If an obvious gain can be made, it should be considered,
in context.
 
W

William Ahern

Arthur J. O'Dwyer said:
I think the simplest solution has been determined to be:

if (some_condition)
return (errno = EINVAL, (void *) NULL);

Still, I agree that this is almost certainly obfuscation. Have
you considered my /idiom du jour/,

if (some_condition1)
goto exit_failure1;
if (some_condition2)
goto exit_failure2;
...

exit_failure1:
errno = EINVAL;
return NULL;

exit_failure2:
errno = ENAMETOOLONG;
return NULL;

Very compact, and has the added bonus that all the "exit" code is in
one place. That means it's harder to miss a free(), and easier to
figure out all the possible return codes (which should be documented
anyway, of course, but still..).

yes ;) i use that extensively, tho i usually limit it to a simple "goto
fail", otherwise things seem a little too cluttered to me (trying to juggle
multiple labels). instead i'll initialize the relevent values so that the
fail section knows what to clean up and what not to. and so i might do
something like:

if (cond) {
errno = EINVAL;
goto fail;
}

anyhoo, this has been an interesting thread, at least for myself.

thanx all.

[END OF THREAD]
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top