[comp.lang.c] C style

ok@quintus.UUCP (Richard A. O'Keefe) (01/13/88)

The book
	Professional Software, Volume II:
	    Programming Practice
	by Henry Ledgard with John Tauer
	Addison-Wesley, 1987
	ISBN 0-201-12232-4 (v.2)
	approx US$20

is about matters of programming style.  Most of what is said is
language-independent;  what is language-specific has Pascal,
Modula-2, and   C   versions.  I don't like everything he says,
and the documentation of data structures is not discussed, but
it's well worth reading.

Mind you, while the Example program at the end is about as
clear and beautiful as Pascal/Ada gets, it convinced me that I
should write programs like that (a text formatter) in Lisp...

ian@mucs.UX.CS.MAN.AC.UK (Ian Cottam) (02/18/88)

I would like to update the style list (below) that I give to students.
Any comments welcome.  Flames to /dev/null pls.
Email is best -- I will summerise.
ian@ux.cs.man.ac.uk
___________________

		Good C Style
		============

Note:  Advice that I, and a few others, have given but has been universely
       ignored is indicated by a + rather than a * in the list below!


* choose a personal or project standard for layout and stick to it

+ show the difference between = and == by using asymmetric layout
  for = (e.g. ptr= NULL;)

* use  expr == var  to emphasise and catch some = for == mistakes
  (e.g. x*2 == y)

+ avoid EXCESSIVE use of multiple exits from a context

* do not rely on defaults, e.g. extern/static variables being set to 0

* use void when you are writing a non-value returning function
  (not the default int)

* cast the return values of functions to void if you are certain that
  their result is not needed (have a list of exceptions to this rule
  e.g. printf, free)

* use lint as a matter of course (not just after 10 hours debugging)

* write portable C (Kernel level code, e.g. drivers, excepting.  Put
  all such code in a separate file.)

* use standard I/O (i.e. don't use UNIX specific I/O without good reason)

* don't use a macro if a function call is acceptably efficient

* avoid complex macros

* don't use complex conditional expressions especially ones returning
  non-arithmetic values

+ don't use assignment inside a complex expression
  (e.g. use (chptr= malloc(N), chptr != NULL)
        rather than
            ((chptr = malloc(N)) != NULL)

+ avoid #ifdefs for version control purposes (use a proper version
  control system)

* use typedef to build up (complex) declarations piecemeal

* use enumerations and #define for constant names
  (enum is preferable to #define, especially with ANSI C)

* always check the error status of a system call

* use header files, e.g. <stdio.h>, rather than assume, e.g., -1 == EOF

* try to avoid assuming the world uses ASCII (difficult one this!)

* use casts rather than rely on representational coincidences

* only have one copy of global declarations in a .h file

* do not embed absolute pathnames in program code (at least #define them)

+ use side-effect operations in statement context only
  (exception: the comma operator)

* use static for variables and functions that are local to a file

* use the ``UNIX standard'' for command line argument syntax

* split programs over several files for ``information hiding''

* never fall through the case arm of a switch statement

+ use local blocks to indicate the extent of variables and comments, e.g.
	{ /* this does foo */
		int foovar;
		/* stuff for foo */
	}
	{ /* this does bar */
		...etc...
	}

* don't use the preprocessor to make C look like anything but C

pajari@grads.cs.ubc.ca (George Pajari) (04/12/88)

In article <130@obie.UUCP> wes@obie.UUCP (Barnacle Wes) writes:
>... `=' is NOT always invalid in an if clause:
>
>    if (status = *statreg) {

Article <5981@utcsri.UUCP> by flaps@utcsri.UUCP (Alan J Rosenthal)
> ... better written as "if ((status = *statreg))"
> some people even recommend "if ((status = *statreg), status)"

What is wrong with:

	if ((status = *statreg) != NULL)...

Most compilers I've tried generate the same code as for the original and
this version (I claim) is the most readble (i.e. scanned quickly by a human
with the fewest resulting semantic errors) of the three.

George Pajari

tneff@atpal.UUCP (Tom Neff) (04/13/88)

In article <1982@ubc-cs.UUCP> pajari@grads.cs.ubc.ca (George Pajari) writes:
>In article <130@obie.UUCP> wes@obie.UUCP (Barnacle Wes) writes:
>>    if (status = *statreg) {
>
>What is wrong with:
>
>	if ((status = *statreg) != NULL)...

For what it's worth, the latter is the preferred formulation as recommended 
by Harbison&Steele (2nd ed.) and Gimpel's PC-LINT, among others.  Certain
grizzled veterans may crave the older, less wordy, more "clever" formulation,
but the explicit comparison is easier to read and less likely to be confused
for something else.  If you get into the habit of never writing if(variable),
you don't tend to have =/== accidents, in my experience.  Also, both MSC
and PC-LINT are nice and flag the former usage above as being possibly a
typo.
-- 

Tom Neff 

franka@mmintl.UUCP (Frank Adams) (04/19/88)

In article <126@atpal.UUCP> tneff@atpal.UUCP (Tom Neff) writes:
>If you get into the habit of never writing if(variable),
>you don't tend to have =/== accidents, in my experience.

I wouldn't say "never write if(variable)"; rather "only write if(variable)
if variable is Boolean".  In my opinion, things like if(variable==TRUE) are
abominations.
-- 

Frank Adams                           ihnp4!philabs!pwa-b!mmintl!franka
Ashton-Tate          52 Oakland Ave North         E. Hartford, CT 06108

jep@oink.UUCP (James E. Prior) (04/21/88)

In article <2823@mmintl.UUCP> franka@mmintl.UUCP (Frank Adams) writes:
>In article <126@atpal.UUCP> tneff@atpal.UUCP (Tom Neff) writes:
>>If you get into the habit of never writing if(variable),
>>you don't tend to have =/== accidents, in my experience.
>
>I wouldn't say "never write if(variable)"; rather "only write if(variable)
>if variable is Boolean".  In my opinion, things like if(variable==TRUE) are
>abominations.

Amen!, and I'll go one further

   if (var==TRUE)

is not only abominable, it can be dangerous.  var==TRUE tends to presume
that the only valid values of var are FALSE and TRUE.  There are times
when a var can very intentionally have a non-zero (true) value other than
TRUE (1).  The classic kind of case of this is var=isalpha(c).  The
isxxxxx(c) functions are often defined as macros like:

   #define isxxxxxx(c)  (attribute_array[c] & MASK_XXXX)

Each element of the array consists of a bunch of bit attributes.  The
MASK_XXXX selects the attributes of interest, often yeilding true but
non-one (non-TRUE) values.

-- 
Jim Prior    {ihnp4|osu-cis}!n8emr!oink!jep    jep@oink.UUCP

Pointers are my friend.

tneff@atpal.UUCP (Tom Neff) (04/21/88)

In article <2823@mmintl.UUCP> franka@mmintl.UUCP (Frank Adams) writes:
>I wouldn't say "never write if(variable)"; rather "only write if(variable)
>if variable is Boolean".  In my opinion, things like if(variable==TRUE) are
>abominations.

I agree completely, Frank, and thanks for the correction.  I meant to say
"non-boolean variable" (or at least I realize that now :-)). Of course, now
we could get into the etiquette of  if(boolean-assignment-expression)... 

-- 
Tom Neff			UUCP: ...uunet!pwcmrd!skipnyc!atpal!tneff
	"None of your toys	CIS: 76556,2536		MCI: TNEFF
	 will function..."	GEnie: TOMNEFF		BIX: are you kidding?

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (04/22/88)

In article <255@oink.UUCP> jep@oink.UUCP (James E. Prior) writes:

| Amen!, and I'll go one further
| 
|    if (var==TRUE)
| 
| is not only abominable, it can be dangerous.  var==TRUE tends to presume
| that the only valid values of var are FALSE and TRUE.  There are times
| when a var can very intentionally have a non-zero (true) value other than
| TRUE (1).  The classic kind of case of this is var=isalpha(c).  The

  If you *must* use stuff like this, at least you can write:
	if (var != FALSE)
which is more likely to work. There is only one good reason I can
determine to use code like that: some COBOL programmer wrote the style
specs for your organization.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs | seismo}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

karl@haddock.ISC.COM (Karl Heuer) (04/22/88)

In article <255@oink.UUCP> jep@oink.UUCP (James E. Prior) writes:
>In article <2823@mmintl.UUCP> franka@mmintl.UUCP (Frank Adams) writes:
>>In my opinion, things like if(variable==TRUE) are abominations.
>
>[It] is not only abominable, it can be dangerous.  var==TRUE tends to presume
>that the only valid values of var are FALSE and TRUE.

If this isn't the case, it shouldn't have been declared boolean.  My coding
style is as if `bool' were a true boolean type, and as if assigning anything
other than `YES' or `NO' (yeah, I follow Kernighan, not Wirth) would produce
undefined results.

>There are times when a var can very intentionally have a non-zero (true)
>value other than TRUE (1).  The classic kind of case of this is [isalpha].

Then isalpha() is not a boolean, in my book.  I'm not convinced there's any
good reason for this; it would be trivial to rewrite the function to return a
true boolean.  In most instances this would have zero run-time cost.

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

barmar@think.COM (Barry Margolin) (04/23/88)

In article <3583@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>There are times when a var can very intentionally have a non-zero (true)
>>value other than TRUE (1).  The classic kind of case of this is [isalpha].
>
>Then isalpha() is not a boolean, in my book.  I'm not convinced there's any
>good reason for this; it would be trivial to rewrite the function to return a
>true boolean.  In most instances this would have zero run-time cost.

Well, if you're coding in C, you'll have to live with C's quirks.
Nowhere does the C language specify that any particular non-zero value
should be returned by the isXXX functions to indicate truth.  The
person who wrote isalpha() might have done #define TRUE 2, and you
might have done #define TRUE 1.  Who is to say that you are right and
he is wrong?  The only thing that C specifies is that you must both do
#define FALSE 0.  Remember, C doesn't have real booleans, and #define
TRUE 1 isn't going to change that fact.

I can think of one reason, however, why 1 should be used as a standard
truth value.  Single-bit fields in structures are generally used as
flags, and it would be nice to be able to say flag = isXXX(...) rather
than flag = (isXXX(...) != FALSE).  But this is just a wish; portable
code must currently use the more verbose version.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
uunet!think!barmar

friedl@vsi.UUCP (Stephen J. Friedl) (04/24/88)

In article <20126@think.UUCP>, barmar@think.COM (Barry Margolin) writes:
< I can think of one reason, however, why 1 should be used as a standard
< truth value.  Single-bit fields in structures are generally used as
< flags, and it would be nice to be able to say flag = isXXX(...) rather
< than flag = (isXXX(...) != FALSE).

Techinically, shouldn't TRUE be `1U' if it goes into a bitfield?
I'm not sure but would like to hear some thoughts on this...

-- 
Steve Friedl      V-Systems, Inc.    Gene Spafford for President!
friedl@vsi.com   {backbones}!vsi.com!friedl    attmail!vsi!friedl

flaps@dgp.toronto.edu (Alan J Rosenthal) (04/24/88)

People complaining recently that "if(expr == TRUE)" is ridiculous have
missed the most convincing reason as to why it is ridiculous.  Note
that we are assuming that expr is a boolean expression in the
Indian-Hill-C-Style-Manual-as-annotated-by-Henry-Spencer sense that it
is known to evaluate to one of 0 or 1.

The clearest reason why "if(expr == TRUE)" is ridiculous is simply that
such a test begs the question.  Certainly "expr == TRUE" returns a
boolean result, so if it is necessary to test boolean results in this
fashion then we must write "if((expr == TRUE) == TRUE)"!  And so on,
making the expression infinitely large, which is explicitly prohibited
by the ANSI C standard.

ajr

--
"The goto statement has been the focus of much of this controversy."
	    -- Aho & Ullman, Principles of Compiler Design, A-W 1977, page 54.

wes@obie.UUCP (Barnacle Wes) (04/25/88)

In article <255@oink.UUCP>, jep@oink.UUCP (James E. Prior) writes:
>    if (var==TRUE)
> 
> is not only abominable, it can be dangerous.  var==TRUE tends to presume
> that the only valid values of var are FALSE and TRUE.

Right.  The only really safe way to do this is:

typedef enum boolean {false, true};
...
	boolean foo;
	...
	if (foo == true) { ...

But, of course, then you can test your `booleans' directly too, like

	if (foo) { ...

-- 
    /\              -  "Against Stupidity,  -    {backbones}!
   /\/\  .    /\    -  The Gods Themselves  -  utah-cs!uplherc!
  /    \/ \/\/  \   -   Contend in Vain."   -   sp7040!obie!
 / U i n T e c h \  -       Schiller        -        wes

karl@haddock.ISC.COM (Karl Heuer) (04/25/88)

In article <20126@think.UUCP> barmar@fafnir.think.com.UUCP (Barry Margolin) writes:
>Nowhere does the C language specify that any particular non-zero value
>should be returned by the isXXX functions to indicate truth.

This is correct according to historical precedent and the Jan88 dpANS.  I
disapprove nevertheless.

>I can think of one reason, however, why 1 should be used as a standard
>truth value.  Single-bit fields ...

I think a more important reason is that the boolean operators such as "<" and
"&&" are already guaranteed to return normalized boolean values (i.e. truth is
denoted by 1).  Requiring the isXXX functions to do likewise would follow the
principle of least astonishment.

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

bts@sas.UUCP (Brian T. Schellenberger) (04/25/88)

In article <20126@think.UUCP> barmar@fafnir.think.com.UUCP (Barry Margolin) writes:
|[for one-bit bit fields]
|it would be nice to be able to say flag = isXXX(...) rather
|than flag = (isXXX(...) != FALSE).  But this is just a wish; portable
|code must currently use the more verbose version.

(I will probably start a "if( a = b )"-type flame war for this, but):

I prefer the shorter form:

	flag = !!isXXX(...)

You can think of "!!" as the "truth-value-of" or "convert-to-canonical-boolean" 
operator.

( To forestall 10 "I didn't know there was such an operator" postings:  )
( This is "!" followed by "!":                                          )
(       non-zero -> 0 -> 1                                              )
(              0 -> 1 -> 0                                              )
-- 
                                                         --Brian.
(Brian T. Schellenberger)				 ...!mcnc!rti!sas!bts

. . . now at 2400 baud, so maybe I'll stop bothering to flame long includes.

root@mfci.UUCP (SuperUser) (04/25/88)

Expires:

Followup-To:

Distribution:

Keywords:


In article <601@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes:
>Techinically, shouldn't TRUE be `1U' if it goes into a bitfield?
>I'm not sure but would like to hear some thoughts on this...

TRUE should have the same type and value as the constant expression (0 == 0).
Similarly, FALSE should have the same type and value as the constant
expression (0 != 0).  This principle holds for any language.  In the case
of C, TRUE and FALSE are signed 1 and signed 0, resp.

nevin1@ihlpf.ATT.COM (00704a-Liber) (04/27/88)

In article <3592@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) writes:

>I think a more important reason is that the boolean operators such as "<" and
>"&&" are already guaranteed to return normalized boolean values (i.e. truth is
>denoted by 1).  Requiring the isXXX functions to do likewise would follow the
>principle of least astonishment.

This is a matter of opinion.  Personally, I would rather have the isXXX
macros be required to return their argument if true, since this conveys
more useful information than simplay a TRUE (1) or FALSE (0) value (BTW,
this might not be possible with the isascii macro).

Also, I would rather have all bits set to represent TRUE (such as two's
complement for -1) instead of 1, since I can use the bitwise operators on
boolean values most effectively this way (I'm not expecting this to happen
due to prior art with < and &&, but one can hope).
-- 
 _ __			NEVIN J. LIBER	..!ihnp4!ihlpf!nevin1	(312) 510-6194
' )  )				"The secret compartment of my ring I fill
 /  / _ , __o  ____		 with an Underdog super-energy pill."
/  (_</_\/ <__/ / <_	These are solely MY opinions, not AT&T's, blah blah blah

nevin1@ihlpf.ATT.COM (00704a-Liber) (04/27/88)

In article <364@m3.mfci.UUCP> karzes@mfci.UUCP (Tom Karzes) writes:

>TRUE should have the same type and value as the constant expression (0 == 0).
>Similarly, FALSE should have the same type and value as the constant
>expression (0 != 0).  This principle holds for any language.  In the case
>of C, TRUE and FALSE are signed 1 and signed 0, resp.

Since when does this principle hold for any language??  Take Fortran, for
instance.  If I remember correctly, odd numbers were TRUE and even numbers
were FALSE (or vice-versa; it's really been a long time since I used
FORTRAN), since compilers were required to look at only the least
significant bit when checking for true/false values.  (This may have been
changed for F77; I'm not sure.)

There are many other examples I could cite (Icon, LISP, etc.).  Your
principle only holds for languages which

a) Have a boolean type

and

b) All logical operations result in that boolean type

such as Pascal.  Many (most?) languages do not conform to these
requirements.
-- 
 _ __			NEVIN J. LIBER	..!ihnp4!ihlpf!nevin1	(312) 510-6194
' )  )				"The secret compartment of my ring I fill
 /  / _ , __o  ____		 with an Underdog super-energy pill."
/  (_</_\/ <__/ / <_	These are solely MY opinions, not AT&T's, blah blah blah

root@mfci.UUCP (SuperUser) (04/28/88)

Expires:

Followup-To:

Distribution:

Keywords:


In article <4547@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes:
>In article <364@m3.mfci.UUCP> karzes@mfci.UUCP (Tom Karzes) writes:
>
>>TRUE should have the same type and value as the constant expression (0 == 0).
>>Similarly, FALSE should have the same type and value as the constant
>>expression (0 != 0).  This principle holds for any language.  In the case
>>of C, TRUE and FALSE are signed 1 and signed 0, resp.
>
>Since when does this principle hold for any language??  Take Fortran, for
>instance.  If I remember correctly, odd numbers were TRUE and even numbers
>were FALSE (or vice-versa; it's really been a long time since I used
>FORTRAN), since compilers were required to look at only the least
>significant bit when checking for true/false values.  (This may have been
>changed for F77; I'm not sure.)
>
>There are many other examples I could cite (Icon, LISP, etc.).  Your
>principle only holds for languages which
>
>a) Have a boolean type
>
>and
>
>b) All logical operations result in that boolean type
>
>such as Pascal.  Many (most?) languages do not conform to these
>requirements.

First, let me clarify my previous remarks:

    1.  Obviously they applied to languages for which operations such as
        comparisons are genuine expressions which return a value with a
        particular type.  This rules out very primitive languages in
        which comparisons are only permitted in statements which test
        a condition (although my definitions of TRUE and FALSE could still
        work if they were simple macros rather than genuine constant
        expressions).  My original message pointed this out, but it was
        so obvious that I decided not to belabor the obvious and deleted it.
        Anyway, it's no big deal.

    2.  Consider the remaining languages which permit truth-valued expressions
        and variables.  In such languages, if TRUE and FALSE are not
        predefined symbols or special tokens, it is useful to define
        literals or macros to distinguish their use from, for example,
        the numbers 1 and 0 for languages which use these for their
        canonical TRUE and FALSE values.  In almost all such languages,
        0 == 0 will yield a canonical TRUE and 0 != 0 will yield a canonical
        FALSE.

        If TRUE and FALSE are to be defined by a programmer, they should
        be have the canonical values which are almost always already
        defined by the language in question.  This is independent
        of whether a unique boolean type is defined by the language (as in
        Fortran) or whether it is not (as in C).  It is also independent of
        what values "test" as TRUE or FALSE.  When testing a value for
        truth/falsity, depending on the language it may be cast to some
        appropriate type first or it may not, after which some test is
        performed (for some languages the test may not even be defined for
        inappropriate values even if the type is acceptable).  However, it
        should always be the case that TRUE tests true and FALSE tests false.

        Here is a table of canonical TRUE/FALSE values for various languages:

            Language        True        False       Type
            --------        ----        -----       ----
            C               1           0           int
            Fortran         .TRUE.      .FALSE.     LOGICAL
            Pascal          TRUE        FALSE       BOOLEAN
            Ada             TRUE        FALSE       BOOLEAN
            PL/I            1           0           FIXED
            Bliss           1           0           <untyped>
            Lisp            T           NIL         SYMBOL
            APL             1           0           <scalar>
            etc.

        When testing for truth/falsity, different approaches are taken.
        In C, every value is merely tested for inequality with 0 (even
        doubles), with the appropriate type casts being performed.
        Fortran only defines truth/falsity for .TRUE. and .FALSE.,
        although many implementations define a bit pattern used to
        represent .TRUE. and .FALSE., and then define a test to determine
        truth/falsity of non-canonical logicals, integers, etc.  (Note
        that standard F77 only allows .TRUE. and .FALSE. for logicals.)
        Some implementations test the low-order bit, some test for
        inequality with zero, etc.  Most represent .FALSE. as 0.  Some
        represent .TRUE. as 1, others as -1.  But none of this is defined
        by the standard.  Bliss tests the low order bit.  Lisp tests for
        inequality with NIL.  Etc, etc.

To address the individual points of confusion in the reply to my original
message:

    1.  You obviously don't know much about Fortran.  No, Fortran does
        not define a true/false test (truth predicate) for integers.
        As I mentioned above, some Fortran compilers choose to expose
        the internal representation of LOGICALS, and may even define
        a truth predicate for integers (i.e., they may permit integers
        to be used directly as logical expressions in logical IF
        statements, etc.).  So what?  Even if this WERE defined and
        required by the standard, it would change nothing.  The values
        .TRUE. and .FALSE. still have type LOGICAL and are still the
        canonical TRUE/FALSE values of the language.  The expression
        0 .EQ. 0 evaluates to .TRUE. and 0 .NE. 0 evaluates to .FALSE..

        I believe VAX/VMS Fortran allows any integer value to be stored
        in a logical, and it tests the low order bit when determining
        truth/falsity.  Integers may be tested directly for truth/falsity.
        Canonical TRUE is -1 and canonical FALSE is 0.  Perhaps you
        thought this was required by the standard (it is not).  How
        parochial.

    2.  I'm not familiar with icon.  However, as I mentioned above,
        the canonical TRUE/FALSE values for lisp are T and NIL.  The
        truth test used by lisp is inequality with NIL.  It happens
        that both T and NIL have the same data type (SYMBOL), although
        in object oriented languages such as lisp they could just as
        easily have different data types (there may well be lisps in
        which NIL is not regarded as a SYMBOL).

    3.  I believe I have already dispensed with the notion that a boolean
        type is required for my principle to hold.

Any more questions?

ok@quintus.UUCP (Richard A. O'Keefe) (04/28/88)

In article <371@m3.mfci.UUCP>, root@mfci.UUCP (SuperUser) writes:
>         Here is a table of canonical TRUE/FALSE values for various languages:
>             Language        True        False       Type
[deleted]
>             PL/I            1           0           FIXED
[deleted]
This entry is incorrected.  The right answer is
	      PL/I	      '1'B        '0'B        BIT(1)
PL/I has (fixed-length) bitstrings BIT(N) as a family of data types.
(It is not clear that this was a good idea.)

dg@lakart.UUCP (David Goodenough) (04/28/88)

From article <175@obie.UUCP>, by wes@obie.UUCP (Barnacle Wes):
> In article <255@oink.UUCP>, jep@oink.UUCP (James E. Prior) writes:
>>    if (var==TRUE)
>> 
>> is not only abominable, it can be dangerous.  var==TRUE tends to presume
>> that the only valid values of var are FALSE and TRUE.
> 
> Right.  The only really safe way to do this is:
 
[safe and silly way deleted]

Why can no one see the forest because of all the trees getting in the way?

	if (var != FALSE)

will *_ALWAYS_* work NO MATTER WHAT, and it satisfies those that need to test
their booleans in a strange manner.

	if (var)

is what I use when testing a boolean, and if it's an integer then I say

	if (var != 0)

which is what I usually mean.
--
	dg@lakart.UUCP - David Goodenough		+---+
							| +-+-+
	....... !harvard!adelie!cfisun!lakart!dg	+-+-+ |
						  	  +---+

hutchson@convex.UUCP (05/07/88)

If you must take Fortran in a c newsgroup, at least take Fortran, not some
brain-damaged non-error-checking dialect.

The current ANSI standard(x3.9-1978,"fortran-77") does not allow one to know
the representation of the logical values .true. and .false. (except that it
fits in the same space in memory as an integer value), so each
implementor chooses his own.  I have seen at least these:
   -  sign bit==1 is true, sign bit==0 is false.
   -  lsb==1 is true, lsb==0 is false.
   -  any non-zero value is true, zero is false.
   -  any non-zero is considered true, .true. is canonically all one bits
   -  any non-zero is considered true, .true. is canonically==(int) 1
Reversing the meaning and representation of each of these would be equally
valid.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/12/88)

In article <764@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes:
>Unions should be avoided, as well as non-pre-aligned structures.

I don't know what you mean by a "non-pre-aligned" structure; perhaps
one with holes?  In any case, object alignment is implementation-
dependent and should not be a major concern when designing most
data structures.  Unions have obvious uses for which there is no
acceptable substitute, but one should not go out of one's way to
use a union where one is not required.

>	if( expression operator expression)
>rather than
>	if (expressionoperatorexpression)
>which can break old compilers with either the space after the if or ...

I have never seen a C compiler that had trouble with white space between
the "if" and "(" tokens.  If you insist on "if(" then at least balance
the white space adjacent to the parentheses.

>It is sheer stupidity to depend on the supposed contents of a black box; for
>instance, a compiler.  This generates non-portable and lazy coding practices
>"aw... the compiler'll make it fast..."

To the contrary, the example you quoted showed that non-portable practices
could be avoided without sacrificing efficiency, by relying on the
compiler to produce code optimized for the particular architecture.
If some compiler has a shortcoming in this regard, effort spent to
improve the compiler will have a larger payoff than effort spent in
tweaking individual applications in an attempt to partially compile
the code by hand in advance.

>Ability to understand others code is the difference between a programmer and
>a person who can program.  Writing code for idiots is only good if you are
>an idiot and can do no better, or if you are willing to hire idiots.

You miss the point.  Even the most experienced C programmer stands to
benefit, EVEN WHEN LATER WORKING ON HIS OWN CODE, if the code is
written with maintainability in mind.  The more low-level tricks
contained in a stretch of code, the less maintainable it is even
to the original programmer.

>There is some truth to this, but the key word is "unnecessary".  It is
>also unnecessary for a computer programmer, whose first abstract concept
>should have been bits.  If the person is a C programmer, the second an third
>concepts should have been Hexadecimal and Octal.  Assuming that these
>operations haven't become automatic after experience is silly.  It is equally
>silly to think of a programmer doing bit operations with multiplies instead of
>shifts!

Judging by the errors I have seen in literally millions of lines of C
code, the C programmer who thinks primarily in terms of bits is probably
introducing scads of present and future bugs into his code.

>You have any idea how long a multiply takes on most architectures?

Not normally long enough to worry about.

One should micro-optimize only those sections of code that need it,
when it is clear that the tradeoff against maintainability is worthwhile.
To do this for all source code would be making a poor long-term tradeoff.

kim@msn034.misemi (Kim Letkeman) (11/20/88)

In article <8864@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:

[ much verbiage removed for brevity ...]

> 
> Judging by the errors I have seen in literally millions of lines of C
> code, the C programmer who thinks primarily in terms of bits is probably
> introducing scads of present and future bugs into his code.
> 
> >You have any idea how long a multiply takes on most architectures?
> 
> Not normally long enough to worry about.
> 
> One should micro-optimize only those sections of code that need it,
> when it is clear that the tradeoff against maintainability is worthwhile.
> To do this for all source code would be making a poor long-term tradeoff.

No question about it. Too many people write poorly structured, unclear
(add as many unpleasant labels as you like) code for no better reason than
a perceived need to write the fastest code on the planet. While I realize
that garden-variety 4.77 mhz machines really appreciate fast code (I own
one myself), micro-optimizing a whole program is overkill to say the least.

One only need read Software Tools and Elements of Programming Style to 
realize that for most programs, the vast majority of the code will have
almost no affect on the overall performance of the program (providing of
course that there are no gross algorithmic blunders.) I have found that 
optimization efforts that focused on better algorithms payed off in order 
of magnitude increases in performance while optimizing a single routine 
usually gives back some fraction of 1 percent ...

Kim

peter@ficc.uu.net (Peter da Silva) (01/11/89)

I have been told that the following mechanism for handling nested includes
is unreliable and/or unportable, but for the life of me I can't see how:

graphics.h:
	#ifndef GRAPHICS_H
	#define GRAPHICS_H
	...
	#endif

windows.h:
	...
	#ifndef GRAPHICS_H
	#include GRAPHICS_H
	#endif
	...

menus.h:
	...
	#ifndef GRAPHICS_H
	#include GRAPHICS_H
	#endif
	...

Now this allows a programmer to include windows.h and menus.h, without
having to (a) know they need to include graphics.h, and (b) worry about
graphics.h being included twice.

What's wrong with this picture?
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.   `-_-'
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.                 'U`
Opinions may not represent the policies of FICC or the Xenix Support group.

peter@ficc.uu.net (Peter da Silva) (01/11/89)

This should make the previous message <2688@ficc.uu.net> a little less absurd.

  windows.h:
  	...
  	#ifndef GRAPHICS_H
! 	#include "graphics.h"
  	#endif
  	...
  
  menus.h:
  	...
  	#ifndef GRAPHICS_H
! 	#include "graphics.h"
  	#endif
  	...
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.   `-_-'
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.                 'U`
Opinions may not represent the policies of FICC or the Xenix Support group.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/11/89)

In article <2688@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>I have been told that the following mechanism for handling nested includes
>is unreliable and/or unportable, but for the life of me I can't see how:
>graphics.h:
>	#ifndef GRAPHICS_H
>	#define GRAPHICS_H
>	...
>	#endif
>windows.h:
>	...
>	#ifndef GRAPHICS_H
>	#include GRAPHICS_H
>	#endif
>	...
>menus.h:
>	...
>	#ifndef GRAPHICS_H
>	#include GRAPHICS_H
>	#endif
>	...
>Now this allows a programmer to include windows.h and menus.h, without
>having to (a) know they need to include graphics.h, and (b) worry about
>graphics.h being included twice.
>What's wrong with this picture?

The only thing wrong is your syntax.  You mean
	#include "graphics.h"
in the latter two files.
In fact there is no need to place conditionals around those inclusions,
since the included file will have no effect if it is already in force,
bacause it checks its one-time lockout flag and avoids redefining things
after the first time it's included in a translation unit.

Notes:
	1.  GRAPHICS_H needs to be reserved for this use.  If this
	header is part of an application (as indicated), then you
	just have to keep track of such symbols, perhaps by making
	the rule that the _H suffix is reserved for them.  If you
	were implementing standard headers for a C implementation,
	you would need to use a lockout symbol that's in the
	implementation's reserved name space, e.g. __CTYPE_H.

	2.  If the header just defines macros and structures,
	and declares types of external objects and functions,
	then you don't need to ensure one-time actions, because
	such actions can be repeated safely.  Typedefs are the main
	things that need to be protected against a second invocation.

	3.  Notwithstanding point 2, if the header includes others
	then it should probably use lock-out symbols, to avoid
	infinite recursion if the other headers include THIS one.

	4.  Application headers should NOT include standard headers,
	because many C implementations do not provide idempotent
	standard headers, so bookkeeping becomes a real mess unless
	you adopt the simple rule that all application headers are
	idempotent and never include system headers inside themselves.
	(ANSI C requires the standard headers to be idempotent, i.e.
	includable multiple times with the same effect as a single
	inclusion.)

	5.  Include headers before doing anything else in the source.

	6.  We use this scheme in a major project and it works
	fine.

bill@twwells.uucp (T. William Wells) (01/11/89)

In article <2688@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
: I have been told that the following mechanism for handling nested includes
: is unreliable and/or unportable, but for the life of me I can't see how:
:
: graphics.h:
:       #ifndef GRAPHICS_H
:       #define GRAPHICS_H
:       ...
:       #endif
:
: windows.h:
:       ...
:       #ifndef GRAPHICS_H
:       #include GRAPHICS_H

(Presumably you mean #include "graphics.h".)

:       #endif
:       ...
:
: menus.h:
:       ...
:       #ifndef GRAPHICS_H
:       #include GRAPHICS_H
:       #endif
:       ...
:
: Now this allows a programmer to include windows.h and menus.h, without
: having to (a) know they need to include graphics.h, and (b) worry about
: graphics.h being included twice.
:
: What's wrong with this picture?

Not a thing. The company I work for does this routinely.  Our source
code is ported all over the place (70+ different systems for one
customer alone!) and we've never had a complaint about this.

Strictly speaking, the #ifndef's in the other include files aren't
necessary but they do save on the time needed to skip the useless
files.

---
Bill
{ uunet!proxftl | novavax } !twwells!bill

peter@ficc.uu.net (Peter da Silva) (01/12/89)

In article <9336@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> The only thing wrong is your syntax.  You mean
> 	#include "graphics.h"
> in the latter two files.

Yeah, yeah, I know.

> In fact there is no need to place conditionals around those inclusions,
> since the included file will have no effect if it is already in force,
> bacause it checks its one-time lockout flag and avoids redefining things
> after the first time it's included in a translation unit.

Depends on the number of open files the operating system and 'C' library
allows, and on the cost of opening a file. If the files are large, this
extra set of conditionals might significantly enhance compilation speed.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.   `-_-'
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.                 'U`
Opinions may not represent the policies of FICC or the Xenix Support group.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/12/89)

In article <2700@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
-In article <9336@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
-> In fact there is no need to place conditionals around those inclusions,
-> since the included file will have no effect if it is already in force,
-> bacause it checks its one-time lockout flag and avoids redefining things
-> after the first time it's included in a translation unit.
-Depends on the number of open files the operating system and 'C' library
-allows, and on the cost of opening a file. If the files are large, this
-extra set of conditionals might significantly enhance compilation speed.

Well, a good reason for nonetheless letting the compiler take care of it
for you is that otherwise the user of the header needs to also know the
special lock symbol for the header.  General design principles argue that
the symbol should be the private property of the header.

daw@houxs.ATT.COM (David Wolverton) (01/12/89)

In article <9336@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> 
> 	2.  If the header just defines macros and structures,
> 	and declares types of external objects and functions,
> 	then you don't need to ensure one-time actions, because
> 	such actions can be repeated safely.  Typedefs are the main
> 	things that need to be protected against a second invocation.

You may still want to use the lockout even when it is safe to include
the header more than once.  My gut-level is that your compile time
would be improved if you have long headers WITH lockouts, because then
the preprocessor is effectively the only part of the compiler that must
process/scan that part of the incoming source.  Without lockouts,
the compiler must parse that stuff, do bookkeeping, etc. which adds
to the compile time.

Dave Wolverton
daw@houxs.att.com

leo@philmds.UUCP (Leo de Wit) (01/13/89)

In article <9345@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|In article <2700@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
|-Depends on the number of open files the operating system and 'C' library
|-allows, and on the cost of opening a file. If the files are large, this
|-extra set of conditionals might significantly enhance compilation speed.
|
|Well, a good reason for nonetheless letting the compiler take care of it
|for you is that otherwise the user of the header needs to also know the
|special lock symbol for the header.  General design principles argue that
|the symbol should be the private property of the header.

An argument against this is that if the user of the header does NOT
need to know this, it could possibly #define the same symbol (for
whatever reason). This would result in the header not being included
properly.

The strategy chosen should perhaps be such that one (or more ?) symbol(s)
per file are being reserved for this purpose, so that each module or
header file knows what symbols can / cannot be used.

Peter's argument 'Depends on the number of open files' doesn't seem
valid:  if you don't use the lock symbol, you open the header file;
since the whole header file is #ifdef'ed out, no other files will be
included in the process. So the total number of files open at any time
is at least one more than in the 'use the lock symbol' strategy. If
your system can't handle that one extra open file, you probably had
already too many open (read: abused the #include facility).

                Leo.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/13/89)

In article <918@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>An argument against this is that if the user of the header does NOT
>need to know this, it could possibly #define the same symbol (for
>whatever reason). This would result in the header not being included
>properly.

I assumed that the available name space had already been divided
among "packages" using some general guideline, such as package
prefixes (explained in a previous posting).  You need to take
care of this anyway.  It is easier to cooperate with general
name space partitioning guidelines than to remember specific
symbols.

leo@philmds.UUCP (Leo de Wit) (01/19/89)

In article <9361@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|I assumed that the available name space had already been divided
|among "packages" using some general guideline, such as package
|prefixes (explained in a previous posting).  You need to take
|care of this anyway.  It is easier to cooperate with general
|name space partitioning guidelines than to remember specific
|symbols.

We use that strategy here too, and it is easy to extend the
guideline to include the preprocessor token:

Global functions are prefixed with a 3 letter & underscore prefix, the
3 letters identifying the module. For instance, if you have a queue
handling module, you would name the functions que_.... . The header
file that exports the module's global functions, variables, types &
macros (and is included by the module itself and all others that use
the module) defines the symbol QUE to prevent multiple inclusion.

Never noticed any problems remembering this scheme ...

  Leo.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/20/89)

In article <925@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>Global functions are prefixed with a 3 letter & underscore prefix, the
>3 letters identifying the module.

Four of the six guaranteed significant characters seems like too
great a price.  We use 2-character "package prefixes"; e.g.
	MmAllo
where "Mm" denotes the "Memory manager" package and "Allo" is of
course the specific function of "Allocation".  This has worked
well for me over the decades.

>The header file that exports the module's global functions, variables,
>types & macros (and is included by the module itself and all others
>that use the module) defines the symbol QUE to prevent multiple inclusion.

Short macros like that worry me, because the probability of conflict
with some other use seems too great.  Our package interface definition
header for the "Mm" package uses the symbol MmH_INCLUDED, which is
practically certain not to clash with anybody else.

leo@philmds.UUCP (Leo de Wit) (01/24/89)

In article <9451@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|In article <925@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
|>Global functions are prefixed with a 3 letter & underscore prefix, the
|>3 letters identifying the module.
|
|Four of the six guaranteed significant characters seems like too
|great a price.  We use 2-character "package prefixes"; e.g.
|	MmAllo
|where "Mm" denotes the "Memory manager" package and "Allo" is of
|course the specific function of "Allocation".  This has worked
|well for me over the decades.

The project I'm currently working on has about 80 modules. The average
number of globals per module is certainly less than 80 (more about
10-20 I guess).  So it seems to me in this case it is completely
justified to use more identifying characters for the modules than for
the globals within a module.

As for the number of names possible, this is still 37 * 37 per module
(considering only uppercase, digits and underscore). You can still use
longer names for readability (whilst keeping the first 6 characters
unique).
Using the underscore may seem a bit of a waste, but it makes reading
linker output a lot easier in environments that only support uppercase
(in the link phase).

   [about 3 char inclusion macros ...]
|Short macros like that worry me, because the probability of conflict
|with some other use seems too great.  Our package interface definition
|header for the "Mm" package uses the symbol MmH_INCLUDED, which is
|practically certain not to clash with anybody else.

OK, accepted 8-).

	 Leo.

flaps@dgp.toronto.edu (Alan J Rosenthal) (07/09/90)

hannum@haydn.psu.edu (Charles Hannum) writes:
>"... preprocessor games, ..."?  It's not a game.  It's a perfectly ligitimate
>use of the C preprocessor.  If we weren't supposed to use it, it wouldn't be
>included (based C's minimalist philosophy).

Are you claiming that all of the obfuscated C contest winners are legitimate C
programs?  Would you like to have to maintain them?

hannum@handel.psu.edu (Charles Hannum) (07/13/90)

In article <1990Jul8.145418.3368@jarvis.csri.toronto.edu> flaps@dgp.toronto.edu (Alan J Rosenthal) writes:

   hannum@haydn.psu.edu (Charles Hannum) writes:
   >"... preprocessor games, ..."?  It's not a game.  It's a perfectly ligitimate
   >use of the C preprocessor.  If we weren't supposed to use it, it wouldn't be
   >included (based C's minimalist philosophy).

   Are you claiming that all of the obfuscated C contest winners are legitimate C
   programs?  Would you like to have to maintain them?


No.  Most of them are non-portable.  Coding style is not a function of the
language; it is a function of personal taste and that of the people you work
for, if any.
--
 
Virtually,
Charles Martin Hannum		 "Those who say a thing cannot be done should
Please send mail to:		  under no circumstances stand in the way of
hannum@schubert.psu.edu		  he who is doing it." - a misquote