* * * * *

     99 ways to program a hex, Part 9: C89, const correctness, assertive

This is a minor variation on part 7 [1]—the use of assert():

> /*************************************************************************
> *
> * Copyright 2012 by Sean Conner.  All Rights Reserved.
> *
> * This program is free software; you can redistribute it and/or
> * modify it under the terms of the GNU General Public License
> * as published by the Free Software Foundation; either version 2
> * of the License, or (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
> *
> * Comments, questions and criticisms can be sent to: [email protected]
> *
> *************************************************************************/
>
> /* Style: C89, const correctness, assertive */
>
> #include <stdio.h>
> #include <ctype.h>
> #include <string.h>
> #include <stdlib.h>
> #include <assert.h>
>
> #define LINESIZE        16
>
> static void   do_dump         (FILE *const,FILE *const);
>
> /****************************************************************/
>
> int main(const int argc,char *const argv[])
> {
>   assert(argc    >= 1);
>   assert(argv    != NULL);
>   assert(argv[0] != NULL);
>
>   if (argc == 1)
>     do_dump(stdin,stdout);
>   else
>   {
>     int i;
>
>     for (i = 1 ; i < argc ; i++)
>     {
>       FILE *fp;
>
>       fp = fopen(argv[i],"rb");
>       if (fp == NULL)
>       {
>         perror(argv[i]);
>         continue;
>       }
>
>       printf("-----%s-----\n",argv[i]);
>       do_dump(fp,stdout);
>       fclose(fp);
>     }
>   }
>
>   return EXIT_SUCCESS;
> }
>
> /******************************************************************/
>
> static void do_dump(FILE *const fpin,FILE *const fpout)
> {
>   unsigned char  buffer[BUFSIZ];
>   unsigned char *pbyte;
>   size_t         offset;
>   size_t         bread;
>   size_t         j;
>   char           ascii[LINESIZE + 1];
>
>   assert(fpin  != NULL);
>   assert(fpout != NULL);
>
>   offset = 0;
>
>   while((bread = fread(buffer,1,BUFSIZ,fpin)) > 0)
>   {
>     pbyte = buffer;
>     while (bread > 0)
>     {
>       fprintf(fpout,"%08lX: ",(unsigned long)offset);
>       j = 0;
>       do
>       {
>         fprintf(fpout,"%02X ",*pbyte);
>         if (isprint(*pbyte))
>           ascii [j] = *pbyte;
>         else
>           ascii [j] = '.';
>         pbyte  ++;
>         offset ++;
>         j      ++;
>         bread  --;
>       } while ((j < LINESIZE) && (bread > 0));
>       ascii [j] = '\0';
>       if (j < LINESIZE)
>       {
>       size_t i;
>
>       for (i = j ; i < LINESIZE ; i++) fprintf(fpout,"   ");
>       }
>       fprintf(fpout,"%s\n",ascii);
>     }
>
>     if (fflush(fpout) == EOF)
>     {
>       perror("output");
>       exit(EXIT_FAILURE);
>     }
>   }
> }
>
> /***************************************************************/
>

_Writing Solid Code_ [2] is one of only two programming books that really
change how I write code (the other being _Thinking Forth_ [3] but that's for
another [DELETED-episode-DELETED] post), begining with the liberal use of
assert() to, well, not validate input parameters, but to enforce that they're
valid.

Prior to this book, I wrote defensive code, so prior to reading the book, I
would have coded do_dump() as:

> static void do_dump(FILE *const fpin,FILE *const fpout)
> {
>   /* vars vars vars */
>
>   if ((fpin == NULL) || (fpout == NULL))
>     return;
>
>   /* rest of code */
> }
>

Not very much code (and in this code, useless as well), but in a larger
codebase, it does add up. And it hides problems with the code. The first
project I liberally used assert() I really went crazy with it. The codebase
implemented “window regions” on a text screen, and every routine used
assert() to not only check that I didn't slip in a NULL pointer, but that
every field of all the structures I defined had reasonable values.

And doing so saved me a lot of debugging time in the corner cases, like, what
exactly does it mean to have a “window” that's only one character wide? Or
even a window that's one character wide by one line high? The assert()s would
trip up on all sorts of corner cases like this, and given that I was
programming the code under MS-DOS, an errant pointer could not only crash the
program, but the entire machine (at best—at worst, it could corrupt memory
that wouldn't be detected until some other program ran).

I still use assert()s to this day.

Now, I'll grant you the following bit of code:

> int main(const int argc,char *const argv[])
> {
>   assert(argc    >= 1);
>   assert(argv    != NULL);
>   assert(argv[0] != NULL);
>

is going a bit too far, only because this is guaranteed to be true by the C
standard, and if it's not, I have more pressing issues to worry about.

* Part 8: C99, const and restrict correctness [4]
* Part 10: C99, const and restrict correctness, assertive [5]

[1] gopher://gopher.conman.org/0Phlog:2012/01/15.1
[2] https://www.amazon.com/exec/obidos/ASIN/1556155514/conmanlaborat-20
[3] https://www.amazon.com/exec/obidos/ASIN/0976458705/conmanlaborat-20
[4] gopher://gopher.conman.org/0Phlog:2012/01/16.1
[5] gopher://gopher.conman.org/0Phlog:2012/01/18.1

Email author at [email protected]