[comp.lang.c] Question on ANSI C compatibility

RMANGALD%CLARKU.BITNET@wiscvm.wisc.EDU (11/08/87)

Hi, everybody:

        Well, I don't know if the subject header is totally correct,
because I'm not sure whether the ANSI standard requires what I don't
want to do -- put a "(void)" in front of every function call where I'm
throwing away the return value.  It's a real pain to prefix all the
"printf()"'s, "gets()"'s, etc. with "(void)", so could someone tell me
whether this is necessary for ANSI compatibility, and whether it is good
programming style?

                                Rahul Mangaldas.
                                rmangaldas@clarku.bitnet

chris@mimsy.UUCP (Chris Torek) (11/09/87)

In article <10224@brl-adm.ARPA> RMANGALD%CLARKU.BITNET@wiscvm.wisc.EDU writes:
>... I'm not sure whether the ANSI standard requires what I don't
>want to do -- put a "(void)" in front of every function call where I'm
>throwing away the return value.

It does not *require* it, but it does permit a compiler to complain
about every such occurrence.

>It's a real pain to prefix all the "printf()"'s, "gets()"'s, etc. with
>"(void)", so could someone tell me ... whether it is good programming style?

For some functions, I would say yes; for others, I would say no.
For instance, the value from printf et alii indicates the number
of characters printed, or EOF (or < 0, not sure which) for error.
Hence one should test printf return values for errors or explicitly
state (via `(void)') that one is not concerned with any such error.
On the other hand, it is possible to test for error after calling
printf:

	fprintf(foil, fmt, arg);
	if (ferror(foil)) {
		/* foiled again */
		...

In this case the return value is indeed dull.

Some functions never return errors: strcpy, for instance, copies
its second argument on top of its first, then returns its first.
`(void) strcpy(to, from)' is only interesting if you have not
saved `to' anywhere else:

	char *
	stralloc(int len)
	{
		char *p = malloc(len + 1);

		if (p == NULL)
			stop("out of memory (stralloc)");
		return (p);
	}

	char *
	strsave(char *s)
	{

		return (strcpy(stralloc(strlen(s)), s));
	}

Unfortunately, there is no (standard) way to distinguish between
functions with `interesting' values and those with `dull' values.
Indeed, in 4BSD there is no way at all; this is something I intend
to change.  It looks as though it will be fairly easy to add to
lint a new pragma:

	...
	double	atof(s) char *s; { return (0.0); }
	int	strlen(s) char *s; { return (1); }
		/* DULLVALUE */
	char	*strcpy(to, from) char *to, *from; { return (to); }
	...
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/09/87)

In article <10224@brl-adm.ARPA> RMANGALD%CLARKU.BITNET@wiscvm.wisc.EDU writes:
>... I'm not sure whether the ANSI standard requires what I don't
>want to do -- put a "(void)" in front of every function call where I'm
>throwing away the return value.  It's a real pain to prefix all the
>"printf()"'s, "gets()"'s, etc. with "(void)", so could someone tell me
>whether this is necessary for ANSI compatibility, and whether it is good
>programming style?

1.  ANSI C does NOT require that you explicitly discard unused function
return values by casting to (void).  It does permit you to do so.  Some
code checkers (e.g. "lint") may warn you if you do not do so.

2.  There are only a small number of functions for which ignoring the
return value is a safe thing to do.  Most notable are ones such as
strcpy() that simply return information you already had, so that there
is no pressing reason to look at the return value.

3.  Other functions such as printf() and gets() do return useful
information as their return value.  You should think hard before
deciding to ignore this information (which generally indicates whether
or not the attempted operation succeeded).  It is almost never wise to
ignore the value returned by gets(), for example.

4.  It is not good coding style to simply pour (void) casts into your
code without thinking about them.  It IS good style to always consider
whether or not you need the return value and explicitly discard it by
a (void) cast when you deliberately decide that the value does not
need to be examined.  It is not particularly bad style to not use such
casts, but then someone looking at your code in the future will have
to puzzle out whether or not you simply forgot to test a return value.

levy@ttrdc.UUCP (Daniel R. Levy) (11/10/87)

In article <10224@brl-adm.ARPA>, RMANGALD%CLARKU.BITNET@wiscvm.wisc.EDU writes:
> It's a real pain to prefix all the
> "printf()"'s, "gets()"'s, etc. with "(void)", so could someone tell me...
> whether it is good programming style?
>                                 Rahul Mangaldas.

When you want to do this, "#define" statements can help you, e.g.:

#define Printf	(void) printf
#define Puts	(void) puts

etc.  Then all you have to do is make a global change on the rest of your
source file with an editor.  You probably want to do this with some care,
that is, don't change fprintf to fPrintf, etc.  Use smart pattern matching,
e.g.,

.,$s/\([ <tab>({]\)printf\([( <tab>]\)/\1Printf\2/g

(where <tab> is a real tab)

in ed or vi.
-- 
|------------Dan Levy------------|  Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
|         an Engihacker @        |		vax135}!ttrdc!ttrda!levy
| AT&T Computer Systems Division |  Disclaimer?  Huh?  What disclaimer???
|--------Skokie, Illinois--------|

daveb@geac.UUCP (11/11/87)

In article <9272@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
| Some functions never return errors: strcpy, for instance, copies
| its second argument on top of its first, then returns its first.
| `(void) strcpy(to, from)' is only interesting if you have not
| saved `to' anywhere else:
|  ...
| Unfortunately, there is no (standard) way to distinguish between
| functions with `interesting' values and those with `dull' values.
| Indeed, in 4BSD there is no way at all; this is something I intend
| to change.  It looks as though it will be fairly easy to add to
| lint a new pragma:
| 
| 	...
| 	double	atof(s) char *s; { return (0.0); }
| 	int	strlen(s) char *s; { return (1); }
| 		/* DULLVALUE */
| 	char	*strcpy(to, from) char *to, *from; { return (to); }
| 	...

   There are several (methinks five) disjoint sets of return values:
	1) functions which return 0 or ERR (-1).
	2) functions which return NULL or a valid string.
	3) functions which return EOF (-1) or a valid character.
	4) functions which return a valid positive char/int or a
	   negative char/int as a specific error indication.
	5) functions which return a boolean value (0 or non-zero).

   They also may affect errno: type (1) almost always does, (2) and
(3) sometimes do. 
   Any function which sets errno is a candidate for (a) dullness and
(b) order-of-error related bugs [ie, "not a typewriter"].

    Types (2) through (5) sound like they're not dull, but
[fs]printf is a member of (4), sorta.

   Can we come up with a criteria for "dullness" which lint can
infer by itself? if necessary from the presence of "extern int
errno; return ERR" in a lintlibrary entry?  Or can someone suggest a
better/more-general way?

 --dave

-- 
 David Collier-Brown.                 {mnetor|yetti|utgpu}!geac!daveb
 Geac Computers International Inc.,   |  Computer Science loses its
 350 Steelcase Road,Markham, Ontario, |  memory (if not its mind)
 CANADA, L3R 1B3 (416) 475-0525 x3279 |  every 6 months.

karl@haddock.UUCP (11/19/87)

In article <1804@geac.UUCP> daveb@geac.UUCP (Dave Collier-Brown) writes:
>Can we come up with a criteria for "dullness" which lint can
>infer by itself? if necessary from the presence of "extern int
>errno; return ERR" in a lintlibrary entry?

You seem to believe that the return value of, say, "unlink" should be
considered "dull" because the user can ignore its value and check errno.
This is inappropriate -- the trouble is that it would cause lint to be silent
in the much more common case when the user checks neither the return value nor
errno.

This code is correct, but unavoidably generates a lint warning (though it can
be silenced with a (void) cast):
  errno = 0;
  unlink(fname);
  if (errno != 0) abort();

However, the following incorrect code does not and cannot generate a warning
for the unchecked error return:
  FILE *fp = fopen(fname, "r");
  int c = fgetc(fp);

My conclusion?  Error signaling in C could use a complete overhaul.  I've got
several ideas% on how it could be improved.  It's unfortunate that the
existing setup is carved in stone.  (Will there ever be a "D" language%%?)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

%Some of you may remember that I was going to post them last February.  Sorry,
I never finished writing up the argument.  It's been suggested that I convert
it into a USENIX talk.  We'll see.

%%Please don't followup just to say it should be named "P".

rsalz@bbn.com (Rich Salz) (11/20/87)

>This code is correct, but unavoidably generates a lint warning (though it can
>be silenced with a (void) cast):
>  errno = 0;
>  unlink(fname);
>  if (errno != 0) abort();
Nope.  You're not "allowed" (sic) to check errno unless you know that
the call really failed.  That is:
	if (unlink(fname) < 0 && errno == ENOPERM) perror("Try 'su'"), exit(1);
is legal, albeit gross, code.

I'll now step aside, and watch this degenerate into another isatty()/perror/
"not a typewriter" mudslide...
	/r$
-- 
For comp.sources.unix stuff, mail to sources@uunet.uu.net.

john@frog.UUCP (John Woods, Software) (11/20/87)

In article <1761@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
> In article <1804@geac.UUCP> daveb@geac.UUCP (Dave Collier-Brown) writes:
>>Can we come up with a criteria for "dullness" which lint can infer by itself
> You seem to believe that the return value of, say, "unlink" should be
> considered "dull" because the user can ignore its value and check errno.
> ...This code is correct, but unavoidably generates a lint warning (though it
> can be silenced with a (void) cast):
>   errno = 0;
>   unlink(fname);
>   if (errno != 0) abort();
> 
The preceeding code is absolutely, unequivocably, wrong.  unlink() is not
in the ANSI C spec at all.  P1003.1 and SVID are the relevant specifications,
and they both state that "The value of errno should be checked only after an
error has been indicated" (SVID) "The value of errno should be examined only
when it is indicated to be valid by a function's return value" (P1003.1
Draft 12).  _errno_ is an ERROR indicator, not a success indicator, in these
standards.


--
John Woods, Charles River Data Systems, Framingham MA, (617) 626-1101
...!decvax!frog!john, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"Cutting the space budget really restores my faith in humanity.  It
eliminates dreams, goals, and ideals and lets us get straight to the
business of hate, debauchery, and self-annihilation."
		-- Johnny Hart

rml@hpfcdc.HP.COM (Bob Lenk) (12/16/87)

>Tell me then, o POSIX gurus, whether there are still functions that can return
>-1 on success (e.g. ptrace() and possibly nice())?  If so, does POSIX specify
>what these functions are allowed to do with errno in such a case?

POSIX specifies how errors are to be checked for each function.  Usually,
for a function that is traditionally a system call (section 2 of UN*X)
this is by checking the return value, and if it has a specific value
(most often -1) errno can be checked.  There is generally no specification
of the effect of any successful function on errno (consider isatty(),
should it set errno to ENOTTY when returning 0 as happens on many
current implementations?).

There is one function, readdir(), for which the same return value (NULL)
may indicate an error or a certain type of success (end of file).  This
is sepcifically mentioned, and the correct way to program is to set
errno to 0 before calling readdir() and to check its value after
readdir() returns NULL.

POSIX does not define ptrace() or nice().

		Bob Lenk
		{ihnp4, hplabs}!hpfcla!rml

decot@hpisod2.HP.COM (Dave Decot) (12/17/87)

> I wrote:
> >  errno = 0;
> >  unlink(fname);
> >  if (errno != 0) abort();
> and was informed that this is unportable usage because the POSIX spec
> doesn't guarantee that functions will preserve errno on success.

This is true of POSIX, and of almost all historical UNIX implementations.

> Tell me then, o POSIX gurus, whether there are still functions that can
> return -1 on success (e.g. ptrace() and possibly nice())?  If so, does
> POSIX specify what these functions are allowed to do with errno in such
> a case?

I don't believe POSIX (P1003.1) specifies any such functions, except
possibly sigaction() and signal(), depending on what constants you use
for SIG_ERR, SIG_DFL, and SIG_IGN.

Dave Decot
P1003.1 Working Group Member
hpda!decot