* * * * * 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: sean@conman.org > * > *************************************************************************/ > > /* Style: C89, const correctness, assertive */ > > #include > #include > #include > #include > #include > > #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 Sean Conner at sean@conman.org .