"Growing up in C"

I would like to thank Greg Bacon for saving this article, and converting it to HTML. I didn't have any saved copies of this, and thought I'd lost it.

Path: info.uah.edu!news.msfc.nasa.gov!newsfeed.internetmci.com!howland.erols.net!www.nntp.primenet.com!nntp.primenet.com!mr.net!winternet.com!solon.com!not-for-mail
From: seebs@solutions.solon.com (Peter Seebach)
Newsgroups: comp.lang.c
Subject: Re: How do you leave  teenage-c to  become a full grown c programmer....how?
Date: 4 Oct 1996 12:34:32 -0500
Organization: Usenet Fact Police (Undercover)
Lines: 227
Message-ID: <533hr8$q4m@solutions.solon.com>
References: <nA+CDAApPPVyEwNU@tedros.demon.co.uk>
Reply-To: seebs@solon.com
NNTP-Posting-Host: solutions.solon.com

In article <nA+CDAApPPVyEwNU@tedros.demon.co.uk>,
Ted  <tedros@tedros.demon.co.uk> wrote:
>For those programmers who are self taught c programmer (such as myself)
>may have encountered a problem in becoming an expert programmer (note i
>understand that there are a few who just *become* experts overnight...or
>so i'm told). This is a typical unfinished life history of a c
>programmer which i'd like any expert to finish:

Doesn't sound like what happened to me, but I think it's an interesting
question.

I never planned to learn C.  Long ago, when 4.3BSD was a new system, I used to
play a game called rogue.  Then, someone (a friend of a friend of the family,
or so) gave me a game called "hack" for my PC-type-machine.  I loved it.

My father got me the source.  Now, the source was for a Unix system, and
wouldn't have compiled on a PC.  It was on paper, and I didn't have a scanner.
It was in C, and I didn't know C.

I also didn't know any of these things.  I know how the game played, and I had
something I had been told was the source.  I knew what source was; source was
stuff that looked like
	120 GOTO 2300
	2300 GOTO 120
and told the computer what to do.

The source for hack seemed pretty readable.  It had blocks of English
text which were inside little boxes of asterisks.  It had things like
	Print("You have a strange feeling.");
It had stuff that looked like
	if (u.wep->type == OBJ_IRONBALL)
		tmp += 25;
....

All of this was obvious.  It didn't take time or effort to learn to read it;
it was just plain old instructions for what to do.

But, of course, I knew none of this.  I just read it.  When I wanted to edit
my version of hack, I searched the binary until I found strings, and edited
them, learning that strings are frequently stored next to each other in the
output, and that a string starts at the same place, even if you overwrite it
with other data.

I continued searching, and I found tables, which had the same data in them as
a struct did, in the same order.

But none of this seemed like learning; I was just poking around with a disk
editor.

Years passed.  I made a brief attempt to learn C, because it looked like a way
to make the computer do things, but never got beyond
	char ary[10];
	ary[0] = 'h';
	ary[1] = 'e';
	...

.... and I eventually got bored and did something else.

In college, I started reading news, and reading mail, and playing muds (when
they started being available.)  One day, I was bored, because I couldn't play
a game, because the system wouldn't let me play games during certain hours.
A friend (actually the sysadmin, whom I'd love to get in touch with) showed
me how to copy the source out of /usr/src (we had a source license), type
'make', and have a game I could play.

So, of course, I looked at the source, and it was pretty readable.  I'd been
reading things like that five years ago, and it still looked like English.

I started using ftp (this was back when you ftp'd from uunet.uu.net, not
ftp.uu.net), and downloading things, and I'd say "make" and sometimes they'd
build.  Mostly, they'd give weird warnings.  Not knowing anything about these,
but being mindlessly agressive, I went in with an editor and tried to fix
them, mostly unsuccessfully.  But I kept trying, and started getting pretty
good at it.

At one point, I managed to get permission to install a game on the system, but
I wanted to make it compress save files.  A game I already had (omega; more on
this later) did this, so I started trying to copy the code.  It took me more
than two hours of experimenting with strcat() and strcpy() and system().
But I *did* it.

Later, I started playing more seriously with omega.  Omega was in the same
family as rogue or hack, but much bigger.  It crashed on the computer I had (a
NeXT) at home, so I tried to fix it.  I failed.  I later got a new version,
and it compiled and ran.  Joy!  I was just getting into the game, so I would
read the code, and play the game.  Sometimes I'd look things up in the code.
Sometimes I'd even try to fix bugs.

Eventually, I *could* fix bugs.  Mostly easy ones, but I found a fair number,
and I got pretty good at fixing them.  Then I started adding features.  I
spent a month or two doing very little but modifying the game.

Then, of course, I decided to write my own.  The astute reader will note that
I had never written *ANYTHING* bigger than "hello, world" at this point.
I wanted a game that had objects and rooms and monsters, and an interface like
omega's, and was fun to play.

About a month later, I *still* couldn't get linked lists to work, and I had
tons of problems.  My code wouldn't run, it wouldn't always compile, and I was
mystified.

About then, I ran into a wizard.  A guy named Mike that my father introduced
me to (one of his students) had asked me if I coded, or some such, and I
started explaining my problems.  He started explaining to me *why* they were
problems.  He told me about abstraction.  I didn't really follow, of course.
He showed me an example, and he suggested I try a few techniques.  I did.

The new version, two weeks later, let you cast spells at monsters, attack
them, select weapons and armor, fight, leave town and go to a dungeon, and
do other things like that.  It was limited in ways, but it did things that
even omega (my baseline) didn't do.

I still have that code.  I don't think it runs on any modern machine; it's
full of illegal garbage, and it was horrifically ill-designed.

The 2nd version was my first attempt at ANSI C.  Leaping out to my
better-trained eyes:
	void main
		Where *did* I get that stupid idea?
	char graphchar[] = "X .#Oo()~*6\"=^-+X.|-#^\";,*<>%)}&][{+^\"=!?/@";
		Of course, there was an enum that indexed into it.

I used a cross between 1TBS and GNU indenting.  Too little whitespace.  On the
other hand, it was 213k of code (8659 lines), and it all seemed to work.  Last
I recall, it had no known bugs.  The largest module was my monstrous linked
list hackery.  It was 26k; 95% of it would be handled by my 3k linked list
library today.  25 modules of C, and 7 headers.

Oh, wait; here's the "known bugs" list:

	1] Occasionally an empty space will be marked solid.
	2] Invisible monsters on occasion.
	3] No idea if greedy monsters work...
	4] Traps/loc_vis, and all that. New system?

I think I actually found the bug that caused 1].

(4.  was a comment where I had the problem that a space might
have a door, a monster, an object, and a trap; what to draw?)

I would be happy to put the unaltered source tree up for people to look at;
it's utterly horrible code, but on the other hand, it was my first real
project.  It is moderately curses-dependant.  (Heavily, even.)  It uses a bit
of Unixism, but not much; mostly, you'd just want to remove the signal
handlers for non-standard signals.

Anyway, much time passed.  I had access to a Mac for a while, and I was really
confused, because I couldn't find <curses.h>  I tried including something like
<CursorCrtl.h>, but it wasn't what I wanted, which struck me as insane; I
remember being confused at the way the Mac implementor had hidden the header.

I got an Amiga.  I got a C compiler for it.  I got a curses implementation for
it.  So I started doing a new version of that game (never came close to
completion, but I did get the key-bindings working...)... And somewhere in
here, was the event that moved me from "using C" to "learning C".

I wanted to do some keyboard I/O.  So, I tried to use getchar().  Of course,
it didn't work the way I wanted it to.  So, being thoughtful, I tried to ask
my friends.  The most help anyone could give me was to reccommend getch().  I
tried it.  It did exactly the same thing; observation revealed it was a macro
for getchar().

(The wizard was elsewhere at the time...)

So, like an idiot, I posted to comp.lang.c, asking if there was a portable way
to do it.  (I had learned onough to know I wanted a portable way to do it.)
In my defense, I believe I had the brains to read the group looking for my
answer.  I didn't get a posted answer (or at least, not one which was of any
use), but someone (I'm pretty sure it was either Steve Summit or Chris Torek,
but I'm not sure I have the info anymore...) wrote me a very helpful letter,
pointing out that this was a FAQ, and that there was no way to do this in *C*,
although my platform could probably do it.

It was like a dam burst.  Suddenly, all the little irritations (no <curses.h>
on the mac, having to find one on the amiga, discovering that it had bugs
because it was for the other kind of compiler, getch() doing the "wrong"
thing...) solidified into an understanding that the *language* was different
from the *compiler*.

I ran into that wizard type again, and he started trying to get me to learn
things.  I was interested, now; I had realized that I could be better at these
things, and they could be easier.  The abstraction thing had started to make
sense when I did the 2nd version of my game; now it made a lot *more* sense.

Somewhere in there, I had discovered that my game was unusably slow; it got to
about 2-5 seconds per move.  I optimized it, of course; I moved all of the
refresh-related activities to one point, so all the game would do is flush
screen activity *when you needed to know* -  essentially, before any attempt
to get input, it would update the screen, if a "dirty" bit had been set, then
clear the dirty bit.  This fixed everything, of course.

I started reading comp.lang.c, and comp.std.c.  Somewhere out there, I got a
copy of the FAQ.

I started programming a lot more, and I got a lot better at it.  I had a brief
fling with DOS programming, but gave up; it was too painful.  I got in the
habit of getting and using new versions of gcc.

Much time passed.  Somewhere in the last few years, I started *answering*
questions, not just asking them.  (I still ask a fair number of questions.)

I've written applications.  When I got a chance to run purify on an
application, I found out that it had only one memory bug, which was a memory
leak I had documented as a limitation of the POSIX spec for getenv/putenv.
(You can't tell, when looking at an environment variable, whether or not it
uses allocated space, but when you use putenv() with a piece of memory, you
*lose* that memory, even if it was allocated.)

That application has been in use for 8 months now, and the only bug in it
was that, if you had no .rc file for it, it wouldn't use the right directory
by default.  This bug cannot be recreated without removing the setup file and
running without your enivornment set correctly.  It goes away if you open and
close the options window.  :)  Any other bugs have remained hidden.

That's how I got here; I just *like* programming, I program constantly, and I
enjoy it.  I learned a *lot* from reading the source to Omega; it's a very
good program.  Some bugs, some design flaws, but it uses abstraction and
isolation of code to good effect.

(I'm told it was the original author's first program.)

-s
-- 
Peter Seebach - seebs@solon.com - Copyright 1996 - http://www.solon.com/~seebs
Unix/C Wizard - send mail for help, or send money for consulting!
The *other* C FAQ, the hacker FAQ, et al.  See web page above.
Unsolicited email (junk mail and ads) is unwelcome, and will be billed for.