[comp.lang.c] Seven Original Sins of K&R

mautner@odin.ucsd.edu (Craig Mautner) (09/25/90)

My apologies for the consumption of bandwidth but I wanted
to send this through properly.  The last time it had the wrong
Subject line.

The author of this does not have access to the news groups.
He asked me to post this and see what comments it generates.
Any correspondence should be sent to him at the internet address
included in the header.

-Craig Mautner

//////////////////// Begin Included Message ////////////////////////


              Seven Original Sins of K&R
                 by Philip J. Erdelsky
                Compuserve: 75746,3411
          Internet: 75746.3411@compuserve.com
                  September 22, 1990

The creation of C approximately two decades ago was a 
wondrous event, even if it did not seem so at the time.  
Like all human creations, C was imperfect.  I have 
identified seven Original Sins--minor flaws in C for 
which K&R will eventually have to answer, in this world 
or the next.  I call them original sins because they were 
present when C originated, not because K&R were the 
first to commit them.  Some of these sins have been 
purged from later versions of C, but others remain with 
us.

I am not the first to decry these sins, nor will I be 
the last.  I am merely another in a long series of 
prophets crying in the wilderness.

                           I

The First Original Sin was pitifully weak typing.  
There is no Boolean type in C, so generations of 
programmers have erroneously written something like "if 
(x=5)" instead of "if (x==5)", only to wonder why x 
always seems to be 5, regardless of what has gone 
before.  The "char" type was not specified as either 
signed or unsigned.  This sin has probably wasted more 
CPU time than any other, as savvy programmers learn to 
put a defensive "&0xFF" after every "char" expression 
that needs to be unsigned.  The default type for 
functions should have been "void", not "int", but there 
was originally no "void" type.

Modern compilers have provided partial redemption from 
this sin, usually by issuing warning messages when the 
program appears to be tainted.  But these warnings are 
often false alarms and go unheeded.  There is still no 
Boolean type, and "char" may be either signed or 
unsigned.  Even the new enumeration types are merely 
integers in disguise, just as willing to be mixed as 
matched.

                          II

The Second Original Sin was the failure to make "NULL" 
a keyword.  Beginning C programmers wonder why you have 
to "#include <stdio.h>" in a program that doesn't use 
standard I/O.  Some compilers don't even object when 
you assign an integer constant to a pointer without a 
typecast, especially when the constant happens to be 
zero.  Don't blame the compiler.  The poor thing can't 
tell the difference between a zero integer constant and 
"NULL".

Redemption from this sin is on its way.  Modern 
compilers define "NULL" as "(void *) 0", so there's at 
least some hope of distinguishing it from a plain old 
zero.

                          III

The Third Original Sin was the use of the keyword 
"static" to mark a function or variable as local to 
particular source file.  This is really a trinity of 
sins.  The word "static" doesn't mean local.  It 
conflicts with the other use of the word "static"--to 
mark a variable inside a function as one that actually 
is static, in an accepted meaning of the word.  
Finally, even if the word "local" had been used 
instead, it would have been marking the wrong thing.  
The word "public", or some similar word, should have 
been used to mark the few functions and variables that 
must be made available to the code in other files.  
Other functions and variables should have been local by 
default.  That's how it's done in assembly language and 
other high-level languages, and the reason for it is 
obvious.

From this sin, however, no redemption is in sight.

                          IV

The Fourth Original Sin is the mandatory use of the 
"break" keyword to terminate a "case" clause in a 
"switch" statement.  Omitting it is natural for 
beginning programmers, and sometimes even for 
experienced programmers who have been dabbling in more 
tightly structured languages.  Of course, this causes 
control to fall through to the next case, which is 
occasionally useful but nearly always a mistake, like a 
double exposure in photography.  But the evil goes even 
further.  Often, the "switch" statement is enclosed in 
a "for" or "while" loop.  You want to finish up a 
"case" clause by breaking out of the loop?  You can't 
do it in C, not without breaking out of the "switch" 
statement first!

The solution, not likely to be adopted even in C+++, 
would be to have the compiler put an implicit "break" 
at the end of every "case" clause, and reserve the 
"break" keyword for breaking out of loops, the way God 
intended.

                           V

The Fifth Original Sin was the way functions are 
defined.  The entire parameter list has to be written 
twice.  That's something no programmer should have to 
do unless it's absolutely necessary.  And to compound 
the evil, an untyped parameter defaults to type "int".  
Most programmers have written something like 
"strcmp(s,t)", forgetting the declaration "char 
*s,*t;".  What you wind up with in most cases is, not a 
function that fails, but something worse--a function 
that works as long as pointers and integers are the 
same size, and then fails when you try to port it.

Fortunately, ANSI C permits prototype definitions, but 
the old way is still permitted, at least during a 
transitional period.  Let's hope the transition is 
brief.

                          VI

The Sixth Original Sin was the way conflicts among the 
names of members of different structures were neither 
forbidden nor resolved.  The original K&R said that 
different structures could have members with identical 
names as long as they had identical offsets.  The way 
early compilers implemented this dictum varied.  Some 
compilers would check to see that the offsets were 
indeed identical.  Others simply generated erroneous 
code when they weren't.  Most programmers took the 
safest course by including the structure name--usually 
abbreviated--in every member name.

Modern compilers have atoned for this sin completely by 
keeping a separate member list for each structure type.  
This resolves the conflicts, but a reminder of past 
iniquities persists in the awkward names of structure 
members in UNIX source code and other old C scriptures.

                          VII

The Seventh Original Sin was the eight-character limit 
on distinguishable names, or even fewer than eight for 
externally defined names.  Of course, some such 
limitation was required for efficient implementation, 
but eight characters are not enough.  C was much better 
than Fortran, which allowed only six, but there are 
many pairs of English words with distinct meanings 
whose first eight letters are identical.  The minimum 
number depends on the language, but for English about 
20 should be sufficient.  German programmers need more.

Most modern compilers do have a reasonable limit, but 
some compiler developers have apparently forgotten that 
virtue lies in moderation.  One compiler allows at 
least several hundred characters, maybe more.  That's 
too long.  Compilers are supposed to compile, not test 
the limits of computability by allowing single labels 
to occupy practically the entire computer memory (and 
disk swap area).  An unprintable name--one that won't 
fit on a single line--should also be uncompilable.

                       Epilogue

None of these sins is inconsistent with the philosophy 
of C.  We needn't embrace heresies like Pascal, Modula 
2 or Ada.  But we must abandon the false god of 100% 
upward compatibility.  We must tear down the old temple 
to build a new one.  Then, and only then, will our 
redemption be at hand.

                         Note

This jeremiad is not copyrighted.  You are welcome to 
copy it and pass it on.  I only ask you to leave my 
name and account number on it.  Let me take the 
credit--and the heat.

//////////////////// End Included Message ////////////////////////
-- 
--------------------------------------------------------------------
Craig D. Mautner		UCSD
mautner@cs.ucsd.edu		Dept of CSE, C-014
(619) 534-4526			La Jolla, Ca. 92093

vd09+@andrew.cmu.edu (Vincent M. Del Vecchio) (09/26/90)

> Excerpts from netnews.comp.lang.c: 25-Sep-90 Seven Original Sins of K&R
> .. Craig Mautner@odin.ucsd. (8365)

> None of these sins is inconsistent with the philosophy 
> of C.  We needn't embrace heresies like Pascal, Modula 
> 2 or Ada.  But we must abandon the false god of 100% 
> upward compatibility.  We must tear down the old temple 
> to build a new one.  Then, and only then, will our 
> redemption be at hand.


I don't know about this.  There are (unfortunately) still so many
pre-ANSI compilers and so much pre-ANSI code (not to mention code that
depends on the existence of the other "sins" that you mentioned) in use
that it would be ridiculous for the time being to abandon backward
compatibility.  I like in general the style of the gcc, accepting both
older and newer code with command-line switches to specify explicitly
one or the other.  But pre-ANSI code will be around for a good while to
come.

+----------------------------------------------------------------------------+
| Vincent Del Vecchio     \ Disclaimer: Views expressed are not necessarily  |
| Box 4834                 \ those of any person/group I am associated with. |
| 5125 Margaret Morrison St.\   UUCP: {uunet,harvard}!andrew.cmu.edu!vd09    |
| Pittsburgh, PA  15213      \   BITNET: vd09+%andrew@cmuccvma.bitnet        |
| (412) 268-4441              \   Internet: vd09+@andrew.cmu.edu             |
+----------------------------------------------------------------------------+

gillies@m.cs.uiuc.edu (09/26/90)

Re: Sin #IV

Hey, how else can I write the following amazingly convoluted code
(idea courtest of Harbison & Steele's book, first edition):

main()
{
int x,i;
x=1;
switch(x) {
	case 1:
		for (i=0; i < 10; i++)
	case 2:
		printf("%d ",i);
}
}

And the result:

0 1 2 3 4 5 6 7 8 9

tom@ssd.csd.harris.com (Tom Horsley) (09/26/90)

What? only seven?

In this list, the only one that seems unforgivable to me is VII - short
names, but you left out the most absolutely awful and despicable sin of all:

                    VIII

The eight (and worst) original sin was allowing arrays to (sometimes) be
kind of automatically converted sort-of into pointers to the first element
of the array except when they aren't. Without a doubt this psuedo
equivalence between arrays and pointers that works most of the time except
when it doesn't has caused more confusion and twisted more brains of people
trying to learn C than any other feature.  If I want the address of an
array, why not stick an '&' operator in front of it like I have to do for
EVERY OTHER kind of variable in C?
--
======================================================================
domain: tahorsley@csd.harris.com       USMail: Tom Horsley
  uucp: ...!uunet!hcx1!tahorsley               511 Kingbird Circle
                                               Delray Beach, FL  33444
+==== Censorship is the only form of Obscenity ======================+
|     (Wait, I forgot government tobacco subsidies...)               |
+====================================================================+

rv@erix.ericsson.se (Robert Virding) (09/26/90)

In article <12780@sdcc6.ucsd.edu>, mautner@odin.ucsd.edu (Craig Mautner) writes:
>              Seven Original Sins of K&R
>                 by Philip J. Erdelsky
>
>                           I
> {Weak typing, reaaly no bools and char neither signed or unsigned}
>         The "char" type was not specified as either 
>signed or unsigned.  This sin has probably wasted more 
>CPU time than any other, as savvy programmers learn to 
>put a defensive "&0xFF" after every "char" expression 
>that needs to be unsigned.

This is no REAL problem, if you are going to do operations which
should be unsigned, just declare the variables as 'unsigned char'. A
(really) savvy programmer would *NEVER* put a &0xff after every char
expression. A novice would.

>                          II
>               Some compilers don't even object when 
>you assign an integer constant to a pointer without a 
>typecast, especially when the constant happens to be 
>zero.  Don't blame the compiler.  The poor thing can't 
>tell the difference between a zero integer constant and 
>"NULL".

According to the language definition assigning a zero to a pointer is
perfectly legal, the compiler shouldn't complain. All C compilers I
have seen will complain for any other integer. For the 50 million'th
time in this group, there is no difference between zero and NULL.

>                          III
> {static}

Granted.

>                          IV
> {break}

What's the problem? "break" means break out of the surrounding
while/for/case. The REAL sin is that "break" ignores a surrounding if.
This really can cause problems.

>The solution, not likely to be adopted even in C+++, 
>would be to have the compiler put an implicit "break" 
>at the end of every "case" clause, and reserve the 
>"break" keyword for breaking out of loops, the way God 
>intended.

I hope it is NEVER adopted! Being able to "fall through" is extremely
pratical and saves much code copying or "goto"s. Adding the "break" is
really no problem.

>                           V
> {defining function arguments}
>Most programmers have written something like 
>"strcmp(s,t)", forgetting the declaration "char 
>*s,*t;".  What you wind up with in most cases is, not a 
>function that fails, but something worse--a function 
>that works as long as pointers and integers are the 
>same size, and then fails when you try to port it.

Actually all compilers will complain when you try to USE "s" and "t"
as pointers

>                          VI
> {struct member name conflicts}

True, but extremely practical. Saved typing, no need to define a union
of possible structures and adding union element names to every
reference :-).

>                          VII
>{eight character name limit}

Agreed, but unfortunately C had/has to exist on systems which
themselves limit the name length (in linkers and such). We could I
suppose always say "don't run C on such systems".

>                       Epilogue
>
>None of these sins is inconsistent with the philosophy 
>of C.

If the sins are consistent with the philosophy of C would there
correction then be inconsistent? :-)

mcdonald@aries.scs.uiuc.edu (Doug McDonald) (09/26/90)

In article <4700066@m.cs.uiuc.edu> gillies@m.cs.uiuc.edu writes:
>
>Re: Sin #IV
>
>Hey, how else can I write the following amazingly convoluted code
>(idea courtest of Harbison & Steele's book, first edition):
>
>main()
>{
>int x,i;
>x=1;
>switch(x) {
>	case 1:
>		for (i=0; i < 10; i++)
>	case 2:
>		printf("%d ",i);
>}
>}
>
>And the result:
>
>0 1 2 3 4 5 6 7 8 9

Well, in a C-like language WHERE THE CASES DIDN'T FALL THROUGH
you could write:

>main()
>{
>int x,i;
>x=1;
>switch(x) {
>	case 1:
>		for (i=0; i < 10; i++)
                goto 2;
>	case 2:
>		printf("%d ",i);
>}
>}

Doug McDonald

goudreau@dg-rtp.dg.com (Bob Goudreau) (09/26/90)

In article <12780@sdcc6.ucsd.edu>, mautner@odin.ucsd.edu (Craig Mautner)
writes:
> 
> The Second Original Sin was the failure to make "NULL" 
> a keyword.  Beginning C programmers wonder why you have 
> to "#include <stdio.h>" in a program that doesn't use 
> standard I/O.  Some compilers don't even object when 
> you assign an integer constant to a pointer without a 
> typecast, especially when the constant happens to be 
> zero.  Don't blame the compiler.  The poor thing can't 
> tell the difference between a zero integer constant and 
> "NULL".

Or better yet, how about a new operator named "nil", which takes
a type name (sorry, pointer types only need apply) and which evaluates
to the nil pointer of that type?  For example,

	char *	cp;

	....

	if (cp == nil(char *)) ....


Of course, it's easy enough to implement now as a macro, but think
of all the comp.lang.c articles that could have been avoided by
building it into the language and thus avoiding having the token
"0" do double duty...

----------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

roy@phri.nyu.edu (Roy Smith) (09/27/90)

goudreau@dg-rtp.dg.com (Bob Goudreau) writes:
>> The Second Original Sin was the failure to make "NULL" a keyword.

What about this for a portable way to define NULL:

#define NULL (""[1])

would that work?
--
Roy Smith, Public Health Research Institute
455 First Avenue, New York, NY 10016
roy@alanine.phri.nyu.edu -OR- {att,cmcl2,rutgers,hombre}!phri!roy
"Arcane?  Did you say arcane?  It wouldn't be Unix if it wasn't arcane!"

jh4o+@andrew.cmu.edu (Jeffrey T. Hutzelman) (09/27/90)

rv@erix.ericsson.se (Robert Virding) writes:

>In article <12780@sdcc6.ucsd.edu>, mautner@odin.ucsd.edu (Craig
>Mautner) writes:
>>              Seven Original Sins of K&R
>>                 by Philip J. Erdelsky
>
>>                          II
>>               Some compilers don't even object when 
>>you assign an integer constant to a pointer without a 
>>typecast, especially when the constant happens to be 
>>zero.  Don't blame the compiler.  The poor thing can't 
>>tell the difference between a zero integer constant and 
>>"NULL".
>
>According to the language definition assigning a zero to a pointer is
>perfectly legal, the compiler shouldn't complain. All C compilers I
>have seen will complain for any other integer. For the 50 million'th
>time in this group, there is no difference between zero and NULL.

I know of one compiler (on a 16-bit micro) that will not complain if you
use any other integer or long int.  Pointers on that machine are
(exaclty) same as unsigned long ints, and the compiler lets you say

void function(void)
{
unsigned char *pointer;
unsigned long int integer;

integer=0xE0C000;
for(pointer=integer;!(*pointer && 0x80););	/* wait for keypress */
printf("%#04x",*pointer & 0x7F);	/* strip off flag bit */
integer=0xE0C010;
*pointer=0;	/* reset keyboard latch and/or advance buffer */
}

which will wait for a keypress and print out its ASCII value (on an
Apple IIgs, under ORCA/C 1.0 or 1.1).
-----------------
Jeffrey Hutzelman
America Online: JeffreyH11
Internet/BITNET:jh4o+@andrew.cmu.edu, jhutz@drycas.club.cc.cmu.edu

>> Apple // Forever!!! <<

goudreau@dg-rtp.dg.com (Bob Goudreau) (09/27/90)

In article <1990Sep26.193626.721@phri.nyu.edu>, roy@phri.nyu.edu (Roy
Smith) writes:
> goudreau@dg-rtp.dg.com (Bob Goudreau) writes:
> >> The Second Original Sin was the failure to make "NULL" a keyword.

Please keep your attributions straight; I did not write that sentence.

> What about this for a portable way to define NULL:
> 
> #define NULL (""[1])
> 
> would that work?

Nope; it's not even *defined* behavior to access beyond the end of
an array, which is what you've done.

----------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

maunz@warwick.ac.uk (The Teenage Student WINJA Turbot) (09/27/90)

In article <1990Sep26.193626.721@phri.nyu.edu> roy@phri.nyu.edu (Roy Smith) writes:
>
>What about this for a portable way to define NULL:
>
>#define NULL (""[1])
>
>would that work?

	B	L	E	U	G	H	!	!

This is a joke, right? 
("") is a null-terminated empty string, no?
Functionally equivalent to a char pointer which refers to an ASCII NUL or
char (0), no?
(""[1]) is probably a char pointer to garbage, no?

Either this is a joke, or I have missed the point.
Either way I'm stupid.
I must agree with the poster/author of the 'original sins' about NULL though.
It would be nice if NULL had been made unnecessary by a standardised keyword
such as 'nullpointer' or something. But nobody could have expected K & R to
have crystal balls.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\ \/ /.       \ JANET maunz@uk.ac.warwick.cu / "As the people here grow
 \/\/ I N J A  \ (K R Turner)               / colder..." -- Kate Bush
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

maunz@warwick.ac.uk (The Teenage Student WINJA Turbot) (09/27/90)

In article <1990Sep27.073730.26575@warwick.ac.uk> maunz@warwick.ac.uk (The Teenage Student WINJA Turbot) writes:
>In article <1990Sep26.193626.721@phri.nyu.edu> roy@phri.nyu.edu (Roy Smith) writes:
>>
>>What about this for a portable way to define NULL:
>>
>>#define NULL (""[1])
>>
>>would that work?
>
>	B	L	E	U	G	H	!	!
>
>This is a joke, right? 
>("") is a null-terminated empty string, no?
>Functionally equivalent to a char pointer which refers to an ASCII NUL or
>char (0), no?
>(""[1]) is probably a char pointer to garbage, no?

Well that proves I'm stupid.
I meant (""[1]) is probably a garbage char, honest, your honour!

>Either this is a joke, or I have missed the point.
>Either way I'm stupid.
>I must agree with the poster/author of the 'original sins' about NULL though.
>It would be nice if NULL had been made unnecessary by a standardised keyword
>such as 'nullpointer' or something. But nobody could have expected K & R to
>have crystal balls.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\ \/ /.       \ JANET maunz@uk.ac.warwick.cu / "As the people here grow
 \/\/ I N J A  \ (K R Turner)               / colder..." -- Kate Bush
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

roy@phri.nyu.edu (Roy Smith) (09/27/90)

I wrote:
> #define NULL (""[1])

maunz@warwick.ac.uk (The Teenage Student WINJA Turbot) responded:
>	B	L	E	U	G	H	!	!

	You have such a way with words :-).  It is clear from the various
bits of mail I've gotten, and postings such as this, that folks on this
group don't think too highly of my idea.  Part of the problem is that I
made a braino (similar to a typo, but occuring above the neck); I meant to
write:

#define NULL (""[0])

	It's also clear that people probably won't think too highly of that
either, so I guess we can just drop it.
--
Roy Smith, Public Health Research Institute
455 First Avenue, New York, NY 10016
roy@alanine.phri.nyu.edu -OR- {att,cmcl2,rutgers,hombre}!phri!roy
"Arcane?  Did you say arcane?  It wouldn't be Unix if it wasn't arcane!"

henry@zoo.toronto.edu (Henry Spencer) (09/27/90)

In article <1990Sep26.193626.721@phri.nyu.edu> roy@phri.nyu.edu (Roy Smith) writes:
>What about this for a portable way to define NULL:
>
>#define NULL (""[1])
>
>would that work?

Uh, to do what?  NULL is a null *pointer*, not a '\0' character.

(That should be [0], and in any case this will not work in initializers
because it is not a compile-time expression under the official rules.)
-- 
TCP/IP: handling tomorrow's loads today| Henry Spencer at U of Toronto Zoology
OSI: handling yesterday's loads someday|  henry@zoo.toronto.edu   utzoo!henry

merriman@ccavax.camb.com (09/28/90)

In article <1990Sep26.193626.721@phri.nyu.edu>, roy@phri.nyu.edu (Roy Smith) writes:
> goudreau@dg-rtp.dg.com (Bob Goudreau) writes:
>>> The Second Original Sin was the failure to make "NULL" a keyword.
> 
> What about this for a portable way to define NULL:
> 
> #define NULL (""[1])
> 
> would that work?
> --
> Roy Smith, Public Health Research Institute
> 455 First Avenue, New York, NY 10016
> roy@alanine.phri.nyu.edu -OR- {att,cmcl2,rutgers,hombre}!phri!roy
> "Arcane?  Did you say arcane?  It wouldn't be Unix if it wasn't arcane!"

NULL is a pointer type! NUL is an ASCII character!

jeenglis@alcor.usc.edu (Joe English Muffin) (09/28/90)

tom@ssd.csd.harris.com (Tom Horsley) writes:
>The eight (and worst) original sin was allowing arrays to (sometimes) be
>kind of automatically converted sort-of into pointers to the first element
>of the array except when they aren't.

C's array semantics make perfect sense once you
understand C's *pointer* semantics, which are probably
the most unique (and elegant, IMHO) feature of the
language.

The only wart I can see on the language wrt. arrays
is that

    int foo(bar) char bar[]; { ... }

is legal syntax.

>Without a doubt this psuedo
>equivalence between arrays and pointers that works most of the time except
>when it doesn't has caused more confusion and twisted more brains of people
>trying to learn C than any other feature.

Actually, it's probably _strings_ that have boggled
beginners with backgrounds in BASIC, causing confusion 
and core dumps, dismaying dozens of dumbfounded dopes,
than anything else.  How many times have *you* seen
a C neophyte go nuts trying to figure out why
'string1 = strcat(string2,string3);' doesn't
work as expected?  Of course, strings make perfect
sense too once you understand pointers.

I hardly consider these things an 'original sin.'
They're natural extensions of a fundamental part
of the language, which is really quite simple
to understand.


--jeenglis@alcor.usc.edu

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (09/28/90)

In article <4700066@m.cs.uiuc.edu>, gillies@m.cs.uiuc.edu writes:
> Hey, how else can I write the following amazingly convoluted code
> (idea courtest of Harbison & Steele's book, first edition):

> main()
> {
> int x,i;
> x=1;
> switch(x) {
> 	case 1:
> 		for (i=0; i < 10; i++)
> 	case 2:
> 		printf("%d ",i);
> }
> }

Easily:
    main()
	{
	    int i;
	    for (i = 0; i < 10; i++) printf("%d ", i);
	    exit(0);	/* you shouldn't leave this out */
	}

This was possible because of the assignment x=1.  If that assignment
had been x=2, the effect would have been undefined because i was not
initialised.  That's a good reason not to jump into loops even if C lets you.

-- 
Fixed in the next release.

s64421@zeus.usq.edu.au (house ron) (09/28/90)

goudreau@dg-rtp.dg.com (Bob Goudreau) writes:

>In article <1990Sep26.193626.721@phri.nyu.edu>, roy@phri.nyu.edu (Roy
>Smith) writes:
>> 
>> #define NULL (""[1])
>> 
>> would that work?

>Nope; it's not even *defined* behavior to access beyond the end of
>an array, which is what you've done.

I think he means 

   #define NULL (""[0])

-- 
Regards,

Ron House.   (s64421@zeus.usq.edu.au)
(By post: Info Tech, U.C.S.Q. Toowoomba. Australia. 4350)

henry@zoo.toronto.edu (Henry Spencer) (09/28/90)

In article <1990Sep27.131329.26616@phri.nyu.edu> roy@phri.nyu.edu (Roy Smith) writes:
>... I meant to
>write:
>
>#define NULL (""[0])

Roy, apart from not being a compile-time value, which limits its use,
this is exactly and precisely equivalent to

#define	NULL	0

Might one ask what you are trying to accomplish with it?
-- 
Imagine life with OS/360 the standard  | Henry Spencer at U of Toronto Zoology
operating system.  Now think about X.  |  henry@zoo.toronto.edu   utzoo!henry

rhealey@digibd.com (Rob Healey) (09/28/90)

In article <12777@sdcc6.ucsd.edu> 75746.3411@compuserve.com writes:
>The author of this does not have access to the news groups.
>He asked me to post this and see what comments it generates.
>Any correspondence should be sent to him at the internet address
>included in the header.
>
>              Seven Original Sins of K&R
>                 by Philip J. Erdelsky
>                Compuserve: 75746,3411
>          Internet: 75746.3411@compuserve.com
>                  September 22, 1990
>
	
	Just general comment on the whole document:

	C wasn't designed to be a general purpose language, it was
	designed to help in the porting of an OS. C is a language
	that is more useful for OS work than for intro to programming
	101. C is also not for people who have traditionally had their
	hands held by a compiler, i.e. strong typing and a plethora
	of data types. C is best used in situations where you need
	to avoid strong typing rather than encourage it.

	Most of the complaints in this article would be best solved if the
	author used the correct language for the task, one with strong
	typing and features that held your hands along the way so you
	didn't have to think as hard or be as careful.

	C isn't the only language in the world, use the language that
	best fits your need. These people who want A language to be
	all things to all applications are attacking the problem from
	the wrong angle.

	If I want quick, simple and dirty BASIC does a good job. If
	I want to do a device driver and feel too lazy to use assembly
	I'll use C. If I want to do database work I'll use SQL or
	a 4GL. If I want to do expert systems I'll use LISP, scheme
	or an OOL. As the man said:

		"The right tool for the right job"

	Rather than turning C into the Ada from HELL, use a better
	language for your needs.

		'Nuff said,

			-Rob

Speaking for self, not company.

roy@phri.nyu.edu (Roy Smith) (09/29/90)

henry@zoo.toronto.edu (Henry Spencer) writes:
> Roy, apart from not being a compile-time value, which limits its use,
> this is exactly and precisely equivalent to
> #define	NULL	0
> Might one ask what you are trying to accomplish with it?

	One might, but it probably wouldn't do much good at this point.  As
somebody pointed out to me in email, the first rule of holes is that when
you're in one, you should stop digging.  Suffice it to say it was a
half-baked idea and I wish I hadn't brought it up in the first place.
--
Roy Smith, Public Health Research Institute
455 First Avenue, New York, NY 10016
roy@alanine.phri.nyu.edu -OR- {att,cmcl2,rutgers,hombre}!phri!roy
"Arcane?  Did you say arcane?  It wouldn't be Unix if it wasn't arcane!"

goudreau@dg-rtp.dg.com (Bob Goudreau) (09/29/90)

In article <1990Sep28.112637.10446@zeus.usq.edu.au>,
s64421@zeus.usq.edu.au (house ron) writes:
> goudreau@dg-rtp.dg.com (Bob Goudreau) writes:
> 
> >In article <1990Sep26.193626.721@phri.nyu.edu>, roy@phri.nyu.edu
(Roy
> >Smith) writes:
> >> 
> >> #define NULL (""[1])
> >> 
> >> would that work?
> 
> >Nope; it's not even *defined* behavior to access beyond the end of
> >an array, which is what you've done.
> 
> I think he means 
> 
>    #define NULL (""[0])

... which is still wrong, though at least it's a legal array access.
The NUL character ('\0') is *not* the same as the null pointer.  See
the FAQ posting for details.
 
----------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

userAKDU@mts.ucs.UAlberta.CA (Al Dunbar) (09/29/90)

In article <1990Sep28.112637.10446@zeus.usq.edu.au>, s64421@zeus.usq.edu.au (house ron) writes:
>goudreau@dg-rtp.dg.com (Bob Goudreau) writes:
>
>>In article <1990Sep26.193626.721@phri.nyu.edu>, roy@phri.nyu.edu (Roy
>>Smith) writes:
>>>
>>> #define NULL (""[1])
>>>
>>> would that work?
>
>>Nope; it's not even *defined* behavior to access beyond the end of
>>an array, which is what you've done.
>
>I think he means
>
>   #define NULL (""[0])
>
>--
>Regards,
>
>Ron House.   (s64421@zeus.usq.edu.au)
>(By post: Info Tech, U.C.S.Q. Toowoomba. Australia. 4350)
 
Wait a minute here. Am I missing something? Wouldn't (""?0?)
be a pointer to a null (zero length) string rather than a
NULL pointer (i.e. a pointer not pointing validly)?
 
-------------------+-------------------------------------------
Al Dunbar          |
Edmonton, Alberta  |   this space for rent
CANADA             |
-------------------+-------------------------------------------

salomon@ccu.umanitoba.ca (Dan Salomon) (10/02/90)

In article <sazwbR600Vp6IHhVVV@andrew.cmu.edu> vd09+@andrew.cmu.edu (Vincent M. Del Vecchio) writes:
> I don't know about this.  There are (unfortunately) still so many
> pre-ANSI compilers and so much pre-ANSI code (not to mention code that
> depends on the existence of the other "sins" that you mentioned) in use
> that it would be ridiculous for the time being to abandon backward
> compatibility.

Much more code is going to be written in C than currently exists.
Should we sacrifice all the code to come for the sake of the existing
code?
-- 

Dan Salomon -- salomon@ccu.UManitoba.CA

rns@se-sd.SanDiego.NCR.COM (Rick Schubert) (10/02/90)

[I know I should probably let this go since this is a dead issue, but I
 couldn't resist improving on Henry's answer.  I hope I'm not missing
 something, since I'm surprised he didn't also say what I'm going to.]

In <1990Sep28.144753.23727@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:

>In article <1990Sep27.131329.26616@phri.nyu.edu> roy@phri.nyu.edu (Roy Smith) writes:
>>... I meant to
>>write:
>>
>>#define NULL (""[0])

>Roy, apart from not being a compile-time value, which limits its use,
>this is exactly and precisely equivalent to

>#define	NULL	0

Since (""[0]) is not a compile-time value (I assume that's the same thing
as a constant expression), it is also not a null-pointer constant.
It DOES have the value 0, but the only arithmetic expressions that 
are null pointers are those that are constant expressions evaluating to 0.

-- Rick Schubert (rns@se-sd.sandiego.NCR.COM)

hp@vmars.tuwien.ac.at (Peter Holzer) (10/02/90)

rv@erix.ericsson.se (Robert Virding) writes:

>What's the problem? "break" means break out of the surrounding
>while/for/case. The REAL sin is that "break" ignores a surrounding if.
>This really can cause problems.

Like in:

1:	for (;;) {
2:		/* code */
3:		if (expression) break;
4:		/* more code */
5:	}

If break would break out of the surrounding if, it would not break out
of the loop, so line 3 would just be a noop.

--
|    _	| Peter J. Holzer			| Think of it	|
| |_|_)	| Technische Universitaet Wien		| as evolution	|
| | |	| hp@vmars.tuwien.ac.at			| in action!	|
| __/  	| ...!uunet!mcsun!tuvie!vmars!hp	|     Tony Rand	|

seanf@sco.COM (Sean Fagan) (10/03/90)

In article <1990Sep26.134716.17540@ux1.cso.uiuc.edu> mcdonald@aries.scs.uiuc.edu (Doug McDonald) writes:
>Well, in a C-like language WHERE THE CASES DIDN'T FALL THROUGH
>you could write:
>>switch(x) {
>>	case 1:
>>		for (i=0; i < 10; i++)
>                goto 2;
>>	case 2:
>>		printf("%d ",i);

Which, of course, prints out 0 *only*, which is not what the original did at
all (although, I will admit, the original wasn't very clever, interesting,
novel, obfuscated, but it was rather stupid).

-- 
-----------------+
Sean Eric Fagan  | "Never knock on Death's door:  ring the bell and 
seanf@sco.COM    |   run away!  Death really hates that!"
uunet!sco!seanf  |     -- Dr. Mike Stratford (Matt Frewer, "Doctor, Doctor")
(408) 458-1422   | Any opinions expressed are my own, not my employers'.

dts@quad.sialis.mn.org (David T. Sandberg) (10/03/90)

In article <1990Oct2.040019.1635@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
>Much more code is going to be written in C than currently exists.
>Should we sacrifice all the code to come for the sake of the existing
>code?

Apples and oranges.  Maintaining backward compatibility doesn't
"sacrifice" all future code... it just means that you can't have
every toy construct you may want.  That in and of itself doesn't
prevent you from using the language, and is not nearly so serious
as the problems that would be caused by breaking most every bit
of existing code (a real, quantifiable sacrifice).

-- 
 \\         David Sandberg          \     ,=,       ,=,           \\
 //     dts@quad.sialis.mn.org      /     | |uadric '=,ystems     //
 \\  uunet!rosevax!sialis!quad!dts  \     '=\       `='           \\

henry@zoo.toronto.edu (Henry Spencer) (10/04/90)

In article <3945@se-sd.SanDiego.NCR.COM> rns@se-sd.SanDiego.NCR.COM (Rick Schubert) writes:
>Since (""[0]) is not a compile-time value (I assume that's the same thing
>as a constant expression), it is also not a null-pointer constant.

Wups.  This is not my week.
-- 
Imagine life with OS/360 the standard  | Henry Spencer at U of Toronto Zoology
operating system.  Now think about X.  |  henry@zoo.toronto.edu   utzoo!henry

rns@se-sd.SanDiego.NCR.COM (Rick Schubert) (10/04/90)

In <1990Oct3.172542.26794@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:

>In article <3945@se-sd.SanDiego.NCR.COM> rns@se-sd.SanDiego.NCR.COM (Rick Schubert) writes:
>>Since (""[0]) is not a compile-time value (I assume that's the same thing
>>as a constant expression), it is also not a null-pointer constant.

>Wups.  This is not my week.

In all fairness, though, this didn't occur to me until I saw your posting.

-- Rick Schubert (rns@se-sd.sandiego.NCR.COM)

browns@iccgcc.decnet.ab.com (Stan Brown, Oak Road Systems) (10/05/90)

M>
Followup-To: >

Lines: 29

WARNING:  Nit about to be picked.

In <1990Sep28.144753.23727@zoo.toronto.edu> 
henry@zoo.toronto.edu (Henry Spencer) writes:
>>In article <1990Sep27.131329.26616@phri.nyu.edu> 
  roy@phri.nyu.edu (Roy Smith) writes:
>>>... I meant to
>>>write:
>>>
>>>#define NULL (""[0])
> 
>Roy, apart from not being a compile-time value, which limits its use,
>this is exactly and precisely equivalent to
> 
>#define	NULL	0

Not exactly or precisely.  Every time the first version is invoked, it
creates a separate instance of a one-byte string.  So you chew up static
data storage.  Depending on how your compiler does alignmnet, it may be
two or four bytes each time--or more.  And in large model with Microsoft
C, some combinations of options could put each "" in its own data
segment!

Is that horse dead yet?  Lord knows I've been beating it enough! :-)


The above is my own opinion and not attributable to any other person or
organization.                        email: browns@iccgcc.decnet.ab.com
Stan Brown, Oak Road Systems, Cleveland, Ohio, U.S.A.    (216) 371-0043

bls@u02.svl.cdc.com (Brian Scearce) (10/05/90)

browns@iccgcc.decnet.ab.com (Stan Brown, Oak Road Systems) writes:
>>>In article <1990Sep27.131329.26616@phri.nyu.edu> 
>  roy@phri.nyu.edu (Roy Smith) writes:
>>>>#define NULL (""[0])

This is the same as #define NULL '\0'

>>#define	NULL	0

This is the same as #define NULL 0

Both are different from #define NULL (void *)0

Neither of the suggested #defines are guaranteed to work with
not-all-bits-0-for-NULL implementations if you pass NULL as a
parameter to a function with no prototype in scope.

>Not exactly or precisely.  Every time the first version is invoked, it
>creates a separate instance of a one-byte string.

There's that, too.

henry@zoo.toronto.edu (Henry Spencer) (10/05/90)

In article <26661@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
>This is the same as #define NULL '\0'
>This is the same as #define NULL 0
>
>Both are different from #define NULL (void *)0
>
>Neither of the suggested #defines are guaranteed to work with
>not-all-bits-0-for-NULL implementations if you pass NULL as a
>parameter to a function with no prototype in scope.

There is *no*, repeat *no*, definition of NULL that is guaranteed to
work with not-all-bits-0-for-NULL implementations if you pass NULL as
a parameter to a function with no prototype in scope.  Actually, this
is true even if null pointers are all-0-bits, because they may not all
be the same size.

Repeat after me, 512 times:

	The representation of different pointer types can be different.

	To turn NULL into a valid null pointer of a particular type, the
	compiler must know the exact type that is desired.

	In the absence of prototypes, the only way to give the compiler
	this information in function calls is to explicitly cast NULL to
	the desired type.

	No definition of NULL can ever remove the need for this, and lazy
	programmers are just going to have to learn to put the casts in.

	No legal program can tell the difference between #define NULL 0,
	#define NULL 0L, and #define NULL ((void *)0).

(Even prototypes do not fully remove the need to be aware of this issue,
since varargs functions still need the casts.)
-- 
Imagine life with OS/360 the standard  | Henry Spencer at U of Toronto Zoology
operating system.  Now think about X.  |  henry@zoo.toronto.edu   utzoo!henry

henry@zoo.toronto.edu (Henry Spencer) (10/05/90)

I wrote:
>	No legal program can tell the difference between #define NULL 0,
>	#define NULL 0L, and #define NULL ((void *)0).

Well, if you want to be really picky, something like "sizeof(NULL)" can,
but no proper use of NULL as a pointer can.
-- 
Imagine life with OS/360 the standard  | Henry Spencer at U of Toronto Zoology
operating system.  Now think about X.  |  henry@zoo.toronto.edu   utzoo!henry

mercer@npdiss1.StPaul.NCR.COM (Dan Mercer) (10/06/90)

In article <1990Oct2.040019.1635@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
:In article <sazwbR600Vp6IHhVVV@andrew.cmu.edu> vd09+@andrew.cmu.edu (Vincent M. Del Vecchio) writes:
:> I don't know about this.  There are (unfortunately) still so many
:> pre-ANSI compilers and so much pre-ANSI code (not to mention code that
:> depends on the existence of the other "sins" that you mentioned) in use
:> that it would be ridiculous for the time being to abandon backward
:> compatibility.
:
:Much more code is going to be written in C than currently exists.
:Should we sacrifice all the code to come for the sake of the existing
:code?
:-- 
:
:Dan Salomon -- salomon@ccu.UManitoba.CA

Regardless of what changes are made to the language there will always
be problems for some in living within the languages limitations.  The
skill of a programmer is in coping with those difficulties.  You
cannot abandon previously written code and expect people to upgrade.
However,  not all the changes proposed would affect previously written
code.  For instance,  implementation of a 

break label;

capability would not affect previously written code.  As for myself,
I am more comfortable using flags (after getting burned by a nasty bug
in PL1 code I wrote breaking out of a label).
-- 

Dan Mercer
Reply-To: mercer@npdiss1.StPaul.NCR.COM (Dan Mercer)
"MAN - the only one word oxymoron in the English Language"