Michael Brudno			8/3/00

More C
---- -

From Tuesday's lecture you may have gotten the incorrect impression that
I don't like C. I like it. It is a very compact, elegant, language which
I code in more than in any other. Yes, it is hard to debug, but the 
advantages which it gives you in terms of power are definetly worth it. 
It is the ++ part of C++ which I don't like. But more on that below.

Pointer Arithmetic
------- ----------

In C you can not only add numbers, you can add pointers (which are numbers,
after all). Consider the following sequence of definitions:

int* c = (int*) malloc(5*sizeof(int));
*c = 5; /* This is the same as c[0] = 5;
c++; /* This means make c point to the next integer */
c[-1] == 5 /* this is true */

Lets take a simple example. Give the array A we want to find the maximum
number in it.

int i, max = A[0];
for (i = 1; i < sizeOfA; i++)
  if (A[i] > max)
      max = A[i];

All well and good. Now for the pointer version.

int *i, *max=A;
for(i=A; i<A+sizeOfA; i++)
  if (*i > *max)
     max = i;

Aaack! And all this does is save a few operations. Don't write code like this!

Obfuscated C
---------- -

On the web there is a page dedicated to bad C code. It is bad. Really bad. 
If you want to see hwo bad for yourself go to www.ioccc.org. IOCCC is the
obfuscated c-code contest. Although completely useless, it is something
fun to look at. Here are a few classics:

/* This will count gotos in an input file. Remember, gotos are considered 
harmful... */

#include  
#include main(togo,toog) int togo; char *toog[]; {char *ogto, tgoo[80]; FILE 
*ogot; int oogt=0, ootg, otog=79, ottg=1;if ( togo== ottg) goto gogo; 
goto goog; ggot: if ( fgets( tgoo, otog, ogot)) goto gtgo; goto gott; gtot: 
exit(); ogtg: ++oogt; goto ogoo; togg: if ( ootg > 0) goto oggt; goto ggot; 
ogog: if ( !ogot) goto gogo; goto ggto; gtto: printf( "%d goto \'s\n", oogt); 
goto gtot; oggt: if ( !memcmp( ogto, "goto", 4)) goto otgg; goto gooo; gogo: 
exit( ottg); tggo: ootg= strlen(tgoo); goto tgog; oogo: --ootg; goto togg; 
gooo: ++ogto; goto oogo; gott: fclose( ogot); goto gtto; otgg: ogto= ogto +3; 
goto ogtg; tgog: ootg-=4;goto togg; gtgo: ogto= tgoo; goto tggo; ogoo: 
ootg-=3; goto gooo; goog: ogot= fopen( toog[ ottg], "r"); goto ogog; ggto: 
ogto= tgoo; goto ggot;} 

/* This will print out pi to 3 decimal places. How? No clue. */

#define _ -F<00||--F-OO--; 
int F=00, OO=00;main( ) {F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() 
			 {    _-_-_-_
			 _-_-_-_-_-_-_-_-_
  		      _-_-_-_-_-_-_-_-_-_-_-_ 
		    _-_-_-_-_-_-_-_-_-_-_-_-_-_ 
	            _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
	            _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
                   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ 
                   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
                   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ 
                   _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ 
                    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
                    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ 
                     _-_-_-_-_-_-_-_-_-_-_-_-_-_
                       _-_-_-_-_-_-_-_-_-_-_-_ 
                           _-_-_-_-_-_-_-_ 
                               _-_-_-_  }>

/* This collection of characters plays tetris */

long h[4];t(){h[3]-=h[3]/3000;setitimer(0,h,0);}c,d,l,v[]={(int)t,0,2},w,s,I,
K=0,i=276,j,k,q[276],Q[276],*n=q,*m,x=17,f[]={7,-13,-12,1,8,-11,-12,-1,9,-1,1,
12,3,-13,-12,-1,12,-1,11,1,15,-1,13,1,18,-1,1,2,0,-12,-1,11,1,-12,1,13,10,-12,
1,12,11,-12,-1,1,2,-12,-1,12,13,-12,12,13,14,-11,-1,1,4,-13,-12,12,16,-11,-12,
12,17,-13,1,-1,5,-12,12,11,6,-12,12,24};u(){for(i=11;++i<264;)if((k=q[i])-Q[i]
) {Q[i]=k;if(i-++I||i%12><1)printf("\033[%d;%dH",(I=i)/12,i%12*2+28);
printf("\033[%dm " +( K-k?0:5 ) , k ) ;K=k;}Q[263]=c=getchar( );}
G(b){for(i=4;i--; ) if( q[i?b+ n[i]:b] ) return 0;return 1;}g( b ){
for(i=4;i--;q[i?x+n[i]:x]=b) ;}main(C,V,a)char* *V ,*a;{h[3]=1000000/
( l=C>1?atoi(V[1]):2);for(a=C>2?V[2]:"jklpq";i;i--)*n++=i< 25||i%12<2?7:0;
srand(getpid());system("stty cbreak-echo stop u" ) ;sigvec(14,v, 0 )
;t();puts("\033[H\033[J");for(n=f+rand( )%7*4;;g(7),u(),g(0)){if(c><0){
if(G(x+ 12 ) ) x+=12;else{g( 7 );++w;for(j=0;j><252;j=12*(j/12+1))
for(;q[++j];)if(j%12==10){ for(;j%12;q[j--]=0 ) ;u();for(;--j;q[j+12]=q[j] ) ;
u();}n=f+rand( )%7*4;G(x=17 ) ||(c =a[5]);}}if(c==*a)G(--x)||++x;if(c==a[1])
n=f+4**(m=n) , G( x ) ||(n=m ) ;if(c==a[2])G ( ++x )||--x;if(c==a[3])
for(;G(x+12);++w)x+=12;if( c==a[4]||c==a[5]) {s=sigblock( 8192 );
printf("\033[H\033[J\033[0m%d\n",w);if(c==a[5])break;for(j=264;j--;Q[j]=0);
while(getchar()-a[4]);puts("\033[H\033[J\033[7m");sigsetmask(s);}}
d=popen("stty -cbreak echo stop \023;cat - HI|sort -rn|head -20>/tmp/$$;mv
/tmp/$$ HI\ ;cat HI" , "w" ) ;
fprintf(d,"%4d on level %1d by %s\n",w,l,getlogin());pclose(d);}>

Why am I show this to you? To prove a very simple point. Writing ugly code 
in C/C++ is easy. Noone will think you are a better programmer because
you use some archaic feature which speeds up your program by 3%. Cleanly 
written code is best. If, however, your urge to write bad code is too strong,
submit it to IOCCC. They have a contest every year.

C++ SUCKS        Section by Jonathan Shewchuk
=========
Over the past few lectures, I've tried to drop occasional hints that C++ is
not quite the miracle it is sometimes rumored to be.  Perhaps some of you have
come to suspect that I might harbor some uncertainty, even disdain, toward the
language.  I must hasten to plead that it is not I alone who entertains, shall
we say, suspicions.  A more articulate explanation than I can give of our want
of enthusiasm is offered by Ian Joyner at "http://www.elj.com/cppcv3/".  To be
precise, the kindly but fair Mr. Joyner offers a 60-page critique that reveals
C++ to be, shall we say, imperfect.  That the shortcomings of C++ take 60 pages
to enumerate is, frankly, discomfiting.

As an encore to my previous harangues, I thought I'd present four more C++
flaws.  The first pertains to C as well.  The second describes how operator
overloading can allow your so-called colleague to change the semantics of the
C++ language under your feet.  The third demonstrates how the attempt to
shoehorn new ideas (objects) into an old syntax (C's) can create confusing
inconsistencies.  The fourth is related to C++ inheritance, which is where many
of the language's worst flaws reside.

Assignment vs. Equality
-----------------------
In most languages, it's easy to get assignment and equality mixed up.  In
C/C++, where "=" is assignment (though to the rest of civilization it's
equality), confusing the two is even easier.  A language should be designed to
catch such errors.

Unfortunately, in C/C++, "==" is just an operator that returns "1" if its
operands are equal, "0" if not.  The "if" statement and other conditionals
interpret zero as falsity, and any other number as truth.  Hence, if you write

  if (x = y) { ... }

you have not, in the compiler's eyes, committed an error.  The compiler will
set x equal to y, and will execute the clause if y is non-zero.  A good C
compiler might print a warning message to ask if you perhaps meant "==", but it
will still produce a program.  Some C compilers won't warn you at all.  (Java,
happily, won't compile this statement unless x happens to be a boolean.)

Conversely, if you write

  x == y;

you have written a perfectly valid C statement that does nothing.

Operator Overloading
--------------------
A feature of C++ you will often hear sold as a good thing is the ability to
redefine operators such as +, =, ++, and [] to have special meanings.

  Dancer &Dancer::operator+(Dancer &d) { ... }

This allows you to write strings like "d1 + d2" instead of "d1.add(d2)".  The
advantage is that your code is...slighly shorter.

The danger is that, in many cases, it's not apparent that a function is being
called--especially when the = (assignment) operator is overloaded.  This makes
maintainability and modularity difficult, because programmers rarely think to
include assignment in their interface descriptions.  One team member may not
know that another team member has changed the meaning of the assignment
operator, and thus have to deal with very mysterious bugs.  Recall that the
same problem arises from the use of copy constructors in initializers (like
"Dancer x = y;").

C++ Syntax at its Slobbering Foulest
------------------------------------
A C++ object whose constructor takes parameters is constructed much like a Java
object.

  Dancer *michaelJackson = new Dancer(36000000, weird);

However, if the constructor takes no parameters, the parentheses are optional.

  Dancer *reindeer = new Dancer();                     // Constructor is called
  Dancer *solidGold = new Dancer;    // No (), but constructor is called anyway

You can also declare a C++ object on the stack.  Even though it's on the stack,
a constructor is still called if one with the appropriate signature exists.
If the constructor takes parameters, these are passed as usual.

  Dancer markMorris(0, excellent);                     // Constructor is called

However, if the constructor takes no parameters, you may NOT use parentheses.

  Dancer johnTravolta;                                 // Constructor is called

Why?  If you did use parentheses, you would be doing something completely
different from what you'd intended to do.  The reason is because this syntax
was already reserved for a completely different purpose in C.

  Dancer johnCandy(); // No constructor, no variable declared, no satisfaction!

Can you tell why?

C++ Member Functions Aren't Virtual by Default, but Can Be Replaced
-------------------------------------------------------------------
The nature of inheritance in Java, and of virtual functions in C++, is that a
method/member function can be overridden in an inherited class.  Two or more
methods having the same name exist, but the compiler calls the right one
according to the type of the _object_ with which the method is invoked.

However, member functions are not virtual by default in C++.  If you forget to
declare a member function virtual, and then try to override it later, it will
really be _replaced_.  Replacement is different from overriding:  the member
function that's chosen is determined not by the dynamic type (the class of the
object), but by the static type of the variable with which the method is
invoked.

  class X {                  |     class Y : public X {
  public:                    |     public:             
    virtual void virt();     |       void virt();      // overrides X:virt()
    void nonvirt();          |       void nonvirt();   // replaces X:nonvirt()
  }                          |     }                   

Y y;
X *xp = &y;     // Both pointers point to an object of type Y.
Y *yp = &y;

yp->virt();     // Calls Y:virt(), as you would expect for objects of class Y.
xp->virt();     // Calls Y:virt(), as you would expect for objects of class Y.
xp->nonvirt();  // Calls X:nonvirt(), even though the object is of class Y!!!!

Why is this bad?  It makes it very difficult for a programmer (especially one
who didn't write these classes) to determine which function is being called in
a particular context.  The information required to figure out which member
function is being invoked by "xp->nonvirt();" may be spread over several files.
Even though "nonvirt()" is defined in class Y, a programmer who's studied class
Y cannot be sure how "xp->nonvirt()" behaves without looking at class X as
well.  A programmer who isn't aware of this quirk of C++, or who forgets to
look, might create some truly mystifying bugs.

Another reason why it's bad:  you can accidentally replace a member function in
a superclass if you don't realize it's there, by unwittingly creating a new
member function with the same name.  The compiler won't warn you.