User Tools

Site Tools


c

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
c [2015/12/31 09:20]
admin [Single return point]
c [2020/10/17 09:05] (current)
114.119.151.1 ↷ Links adapted because of a move operation
Line 1: Line 1:
-=====Writing C=====+====== Decent C======
 I've been writing c for over 30 years, but it can still be a nightmare - the problems are well documented elsewhere. The discipline of writing device drivers for Linux/Solaris/HP-UX/AIX etc etc and crypto code for RSA (thanks, Michael for the excellent code-reviews - I learned so much) have led to a few points that help make code clearer, easier to maintain and less likely to contain memory leaks and other faults. I've been writing c for over 30 years, but it can still be a nightmare - the problems are well documented elsewhere. The discipline of writing device drivers for Linux/Solaris/HP-UX/AIX etc etc and crypto code for RSA (thanks, Michael for the excellent code-reviews - I learned so much) have led to a few points that help make code clearer, easier to maintain and less likely to contain memory leaks and other faults.
 ====Single return point==== ====Single return point====
-Every routine should have a single return point. All possible execution paths go through this point and therefore have the chance to have resources released. The main thing, of course, is to free resources that are no longer needed eg memory that has been malloc()'ed, file/socket descriptors and in device drivers, to release DMA and interrupt resources.+Every routine should have a single return point. All possible execution paths go through this point and therefore have the chance to have resources released. The main thing, of course, is to free resources that are no longer needed eg memory that has been malloc()'ed, file/socket descriptors and in device drivers, to release DMA and interrupt resources. See the following example.
 ====Error responses should be close to the failing statement==== ====Error responses should be close to the failing statement====
 and not dangling at the end of some huge }}}} block ten pages away!!! Deal with it _now_, to hell with making the Happy Path clearer - the Happy Path is easy, it's the errors that make the thing beastly. and not dangling at the end of some huge }}}} block ten pages away!!! Deal with it _now_, to hell with making the Happy Path clearer - the Happy Path is easy, it's the errors that make the thing beastly.
 ====Use goto==== ====Use goto====
-Don't be (too) scared to use 'goto'. The evils of 'goto' were all too well propagated when "structured programming" got started in the 70-80's. But it still has a vital role in simplifying code. Until modern languages catch up with google 'go's 'defer' statement (which stacks up clauses to be run on routine exit) this technique is essential - even in C++!!!+Don't be overly scared to use 'goto', it's actually quite useful when used appropriately. The evils of 'goto' were all too well propagated when "structured programming" got started in the 70-80's. But it still has a vital role in simplifying code. For example, until modern languages catch up with google go's 'defer' statement (which stacks up clauses to be run on routine exit) this technique is essential - even in C++!!!
  
-The best place for the return logic block is at the end of the routine (the single exit point, remember?) under a label. When errors are detected and the routine needs to bail, a simple "goto end" (or "goto hell" if you prefer) allows cleanup to be done. This also eliminates tortuous if-else sphaghetti code and ties the remedial action close to the failing statement - not dangling at the end of some nested }}}} block 10 pages down the code.+The best place for the return logic block is at the end of the routine (the single exit point, remember?) under a label. When errors are detected and the routine needs to bail, a simple "goto end" (or "goto hell" if you prefer) allows cleanup to be done. This also eliminates tortuous if-else spaghetti code and ties the remedial action close to the failing statement - not dangling at the end of some nested }}}} block 10 pages down the code.
  
-Do it even when it looks stupidly simple - when the routine is made more elaborate then the logic is in place and you will remember to cleanup:+Do it even when it looks stupidly simple - later, when the routine is made more elaboratethen the logic is in place and you will remember to cleanup:
  
-<code> +<code c
-/* @return 0 if filename is foobarable. Otherwise 1. +/* @return 0 if file is foobarable. Otherwise 1. 
  */  */
 int is_not_foobar_able( int is_not_foobar_able(
-    char *filename, /**&lt; filename IN name of file to test */ +    char *filename, /** filename IN name of file to test */ 
-    int verbose,    /**&lt; verbose IN set to non-0 to make it verbose */ +    int verbose,    /** verbose IN set to non-0 to make it verbose */ 
-    int bufsize     /**&lt; malloc this much heap */+    int bufsize     /** malloc this much heap */
     )      ) 
 { {
     struct stat buf;     struct stat buf;
-    int retval = 1;+    int retval = 1; /* default return is 'error' */
     char *bigbuf = NULL; /* just to demonstrate an error return with cleanup */     char *bigbuf = NULL; /* just to demonstrate an error return with cleanup */
  
     if (verbose) printf("Checking %s\n", filename);     if (verbose) printf("Checking %s\n", filename);
  
-    if (stat(filename, &amp;buf)) {+    if (stat(filename, &buf)) {
         goto end; /* error return */         goto end; /* error return */
     }     }
  
-    if ((buf.st_mode &amp; S_IFMT) == S_IFREG) { +    if ((buf.st_mode & S_IFMT) == S_IFREG) { 
         /* it's a regular file */         /* it's a regular file */
-        if ( !(buf.st_mode &amp; 0111)) {+        if ( !(buf.st_mode & 0111)) {
             goto end; /* error return */             goto end; /* error return */
         }         }
Line 55: Line 55:
  
 ====Initialise all variables==== ====Initialise all variables====
-Especially pointers which should be initialised to NULL. Then, the return logic in the single exit point can clean them up if non-NULL.+Especially pointers which should be initialised to NULL. Then, the return logic in the single exit point can clean them up if non-NULL. See the previous example.
 ====Declare a maximum of one variable per statement==== ====Declare a maximum of one variable per statement====
 The dangers are things like: The dangers are things like:
Line 71: Line 71:
     char *str;     char *str;
 } SAFE_STRING; } SAFE_STRING;
 +</code>
 This also has the benefit of potentially being more efficient of CPU cycles as it avoids the constant and repeated use of strlen(). Think about it. This also has the benefit of potentially being more efficient of CPU cycles as it avoids the constant and repeated use of strlen(). Think about it.
 ====Return an error code==== ====Return an error code====
 If at all possible, always return an int to indicate success or failure eg. 0 for success, anything else for an error. Or return a pointer on success and NULL on failure. If at all possible, always return an int to indicate success or failure eg. 0 for success, anything else for an error. Or return a pointer on success and NULL on failure.
-====Document all parameters and return values+====Document all parameters and return values====
 Just do it. No, the source code is not enough documentation. And keep it up to date as you code. You might as well use doxygen format in case the employer cultivates a taste for it. Just do it. No, the source code is not enough documentation. And keep it up to date as you code. You might as well use doxygen format in case the employer cultivates a taste for it.
 ====exit values etc==== ====exit values etc====
 Unless you've got a really good excuse, the program should exit(0) on success and non-zero on failure (note that exit code can only be up to 8-bits long). Unless you've got a really good excuse, the program should exit(0) on success and non-zero on failure (note that exit code can only be up to 8-bits long).
-As with [[DecentBash]], the program should send error messages (prefixed with the program name) to stderr, output to stdout. It should recognise -h (no matter what) and possibly --help and print help to stdout (and return 0!). -v and --verbose are also pretty standard.+As with [[unixscripts:3-decentbash]], the program should send error messages (prefixed with the program name) to stderr, output to stdout. It should recognise -h (no matter what) and possibly --help and print help to stdout (and return 0!). -v and --verbose are also pretty standard.
  
 In fact, why not use argp(3) to make option processing easy? In fact, why not use argp(3) to make option processing easy?
Line 85: Line 85:
 Functions must fit on a single editor page. OK I have a hi-res screen and get 70 lines on it, but that's OK. I also stick to K&R code layout (as above) so more context fits on a single screen. Functions must fit on a single editor page. OK I have a hi-res screen and get 70 lines on it, but that's OK. I also stick to K&R code layout (as above) so more context fits on a single screen.
 ====valgrind==== ====valgrind====
-You're not done until valgrind says you're done!!+You're not done until valgrind says you're done and your program leaks no memory!!
 ====Keep it simple==== ====Keep it simple====
-As Kernighan'is reputed to have said, debugging (and tuning) is twice as hard as coding so if you code to the limit of your ability, then you will not be able to debug or tune it! This sin is most often commited by new programmers eager to show how clever they are. I'm not usually very impressed.+As Kernighan is reputed to have said, debugging (and tuning) is twice as hard as coding so if you code to the limit of your ability, then by definition, you will not be able to debug or tune it! This sin is most often commited by new programmers eager to show how clever they are. I'm not usually very impressed.
 ====Have it reviewed==== ====Have it reviewed====
-I've learned so much from having others (contructively) critique my code.+I've learned so much from having others (constructively) critique my code.
 ====Links==== ====Links====
 Here's someone who thinks the same way: Here's someone who thinks the same way:
c.1451553628.txt.gz · Last modified: 2015/12/31 09:20 by admin