From 7e0f021a9aec35fd8e6725e87e3313b101d26f5e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Sun, 27 Jan 2008 11:37:44 +0100 Subject: Initial import (2.0.2-6) --- reference/C/CONTRIB/SNIP/c_port.txt | 340 ++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100755 reference/C/CONTRIB/SNIP/c_port.txt (limited to 'reference/C/CONTRIB/SNIP/c_port.txt') diff --git a/reference/C/CONTRIB/SNIP/c_port.txt b/reference/C/CONTRIB/SNIP/c_port.txt new file mode 100755 index 0000000..bc78baa --- /dev/null +++ b/reference/C/CONTRIB/SNIP/c_port.txt @@ -0,0 +1,340 @@ + +=====[ Ed Hopper's BBS #1 ]=====[ 8-04-91 ]=====[ 9:55.22 ]===== + + +Date: 08-02-91 (09:40) C-Lang Number: 26406 (Echo) + To: ALL +From: JOSEPH CARNAGE Read: 08-03-91 (10:56) +City: DUNEDIN FL Last On: 02-28-91 (22:53) +Subj: Portable, clean code #1 + + How to write portable clean source code + --------------------------------------- + +A common concern with programmers new to Axiom is writing portable +code. There a number of tricks and guide lines which may help with +this. In no particular order: + + -- Use full ANSI prototypes with all arguments declared. For + function pointers, declare their expected arguments. For + prototypes for functions which accept function pointers, do not + declare the expected arguments for the function pointer + argument. + + It is good practice to put dummy variable names in prototypes as + this adds readability. + + -- Explicitly type all variables and functions. Never rely on them + defaulting to int. + + -- Pay a little care to the ternary operator ? :, and parenthesize + heavily. A very few compilers have problems with the default + order of evaluation for the ternary operator. + + -- Never ever name a variable identically to a function. This is + most especially true of statics or globals. This sort of error + can cause weird hidden linker problems which cause bizarre + results at runtime and are difficult to trace. + + -- Never ever name a screen, form, data field etc identically to a + variable or function as this can cause weird and non-reported + linker errors in the final executable which can be very + difficult to locate. + + -- Do not nest comments. If you want to block off a section of + code temporarily, use #ifdef/#endif. eg. + + ...code... + #ifdef JUNK /* Unwanted code */ + ...more code... + #endif /* JUNK */ + ...yet more code... + + -- Run PC-Lint on all code, and handle ALL errors and warnings. + + -- Read the "Frequently Asked Questions" document and understand + fully. + + -- Read K&R thoroughly and everywhere it mentions "implementation + dependant", or "new" features not "supported by all compilers", + avoid those areas. Examples are bit fields, passing structures + to functions, returning structures from functions, and the + volatile type. These are not supported by all compilers. + + -- In areas where the ANSI standard advances on the old K&R, but + still allows the K&R form, follow K&R. An example is using + function pointers. If you are unsure what areas this covers, + don't worry, just stick with K&R. eg. + + Given: + + int (*prj_afunc) (); /* Pointer to function which returns int */ + + ANSI allows the pointed to function be called as so: + + prj_afunc (xxx, yyy); + + K&R specifies that it should be called: + + *prj-afunc (xxx, yyy); + + Use the K&R form, which ANSI still allows, and all compilers + support. + + -- Do not use the new // comments. Stay with the /* comment */ + form. + + -- Do not indent #ifdefs, #defines, #pragmas, or other preprocessor + directives. Some compilers allow code as such: + + #ifdef DEBUG + #define TEST 1 + #endif + + or + + #ifdef FINAL + # ifdef DEBUG + # define TEST 1 + # endif + #endif + + But by no means all. Use white space if needed to delineate + #ifdef/endif blocks and comment liberally: + + #ifdef FINAL + + #ifdef DEBUG + #define TEST 1 + #endif /* DEBUG*/ + + #endif /* FINAL */ + + -- Do not use the ANSI string literal concatenation features. eg. + + printf ("This is ANSI but" + "unportable code.\n"); + + for long string literals. Under ANSI the the compiler should + concatenate the two string literals into one, but not all ANSI + compilers support this feature yet. If you need a very long + string literal use a form as so: + + printf ("%s%s", + "this is", + "portable code.\n"); + + or just use a very long string literal. + + -- Stay away from ints except for trash and temp values. Ints vary + in size depending upon the memory model under DOS, and legally + may be any size between shorts and longs inclusive. + + Try to use shorts or longs if possible, as these are of fairly + constant size on most platforms. On most platforms, but by no + means all, shorts are usually words and longs word pairs. + + -- Beware of assigning a pointer of one type to a pointer of a + higher type. Most platforms seem to insist that the addresses + stored in pointers are alligned per the pointer's base type. + + What this means is that the value stored in a pointer to a long, + in itself will be alligned as a long is. If longs are alligned + on even word boundaries, then so will the value of long pointer. + + This can result in memory allignment errors which can be + extremely difficult to track down. Casting will not help. + + DOS has few memory allignment requirements, but for Unix and VMS + you can expect types to be alligned to their sizes (see the + compiler manuals for specifics). What this means to pointers is + that with code such as: + + short *pj2_value; /* Assume that shorts are alligned to words */ + long *pj4_number; /* and longs to even word boundaries */ + + pj4_number = pj2_value; + + that the value assigned to pj4_number may be as far as two bytes + different from that in pj2_value. ie + + Let's say that the address stored is pj2_value is: + + *pj2_value == 0000:0006 /* Alligned to word */ + + and you make the assignment: + + pj4_number = pj2_value; + + After this, the value of pj4_number will be either 0000:0004, + or 0000:0008 to shift it to even word allignment. The + direction of the shift seems to be compiler/implementation + dependant. + + This bug is often erratic at runtime depending upon the + allignment of automatics. This sort of bug is especically + difficult to track when coming up from a void pointer. + + -- Be wary of relying on memory allignment in structures and + unions. Different compiler implmentations allign differently, + and #pragmas or command line arguments to the compiler can + change allignment at compile time. See the compiler manuals for + specific details. + + This will usually require you to either read in each member + individually, or to perform explicit padding when reading + structure data from disk. eg. lic1.c & v_lic_pad() in the Axiom + library. + + -- Where possible use sizeof(identifier) rather than sizeof(type) + or a #defined constant. This can help in tracing down bugs and + makes for greater readability. eg. + + #define M_DATA 100 + short aj2_numbers[M_DATA]; + + /* This example requires the reader to remember that + aj2_numbers has M_DATA elements, is overly complex, and + presumes that aj2_numbers will always be shorts. This code + will likely break if anything is later changed. */ + + memcpy (aj2_numbers, pj2_input, M_DATA * sizeof (short)); + + /* This is ideal -- sizeof(aj2_numbers) will return the total + space allocated to the array, no matter what the type may be, + or what is changed later. It makes no assumptions of the + reader. */ + + memcpy (aj2_numbers, pj2_input, sizeof (aj2_numbers)); + + -- Beware of comparing structures or unions with functions such as + memcmp() as the padding/allignment spaces will have random and + likely different values. If you need to compare structures + you'll have to do it member by member. + + -- Never assign structures to each other directly. Some compilers + allow structure assignments, some don't. + + struct t_data s_struc1; + struct t_data s_struc2; + + s_struc2 = s_struc1; /* Unportable code */ + + Note however that you can copy structures via pointers as need + be: + + struct t_data *ps_struc1; + struct t_data *ps_struc2; + + ps_struc1 = xxx; + ps_struc2 = yyy; + + *ps_struc2 = *ps_struc1; /* Copy structure 1 to 2 */ + + Rather than assigning each member over individually. The + functions memcpy() and memmove() are other portable ways. + + -- Beware of passing chars or shorts to functions. These get + promoted to ints, and with some compilers problems from there on + out abound horrendously. This is especially true of chars where + some compilers occassionally extract the wrong byte from the + promoted int in the recieving function. + + -- Be careful passing floats to functions as some compilers promote + floats to doubles when passing them as an argument. This can + cause spurious warnings and and strange side effects. + + -- Explicitly cast assignments and expressions as needed, and + carefully watch that you really don't need to make those + identifiers of that type originally. While the promotion order + is constant across implementations, the size of the types + aren't. This can cause difficult side effects. + + If your code needs a lot of casts to get past Lint, then you + probably need to rethink some of your approaches. + + -- Take care to cast all #defined constants if in doubt. For large + values (longs), always cast as longs, or place an 'L' at the end + of the constant. Some compilers handle this area erraticly if + left up to them. + + -- Never ever rely on order of evaluation of function parameters. + This can occur when listing a funtion call as a parameter to + another funtion, or as an unintentional side effect from passing + assignments or function calls as parameters. + + char *pc_modify (char *); + + printf ("This string [%s] becomes [%s]\n", + pc_string, pc_modify (pc_string)); /* Bad code */ + + -- Do not use NULL for anything but pointers. Do not use NULL for + string terminations: use the ASCII constant NUL, '\0', or a + #defined type which equates to that. + + -- Never EVER pass a #defined macro an incremented or decremented + value (++,or --) or an assignment as a parameter. This is + because many #defined macros may reference their arguments + multiple times. This is especially true of the macros #defined + in ctype.h. + + eg. + + #define iscsymf(c) (isalpha(c) || ((c) == '_')) + + If called as so: + + iscsym(var++); + + it will be expanded by the preprocessor to: + + (isalpha(var++) || ((var++) == '_')) + + with var being incremented twice. + + -- Avoid passing any functions incremented or decremented values + ("++" or "--"), or assignments. Some standard and commercial + library calls are actually #defined macros. This type of + "error" can lead to order of evaluation problems as different + compilers process function arguments in different orders. + + -- Explicitly initialise pointers. Do not rely on calloc(), + memset() or other such functions to initialise pointers. + Initialise them explicitly. + +"K&R" as mentioned above refers to "The C Programming Language" +written by Brian W Kernighan & Dennis M Ritchie. + +"PC-Lint" is a commercial version of Lint for the PC, as sold by +Gimpel Software. PC-Lint is generally acknowledged as the tightest +and most discerning Lint on any platform. + +There are several books which cover the areas of portable code which +may also help: + + "C Programming Guidelines" + by Thomas Plum + pub. by Plum Hall + ISBN 0-911537-03-1 + + "Portability and the C Language" + by Rex Jaeschke + pub. by Hayden Books + ISBN 0-672-48428-5 + + "Portable C Software" + by Mark R. Horton + pub. by Prentice Hall + ISBN 0-13-868050-7 + + "Portable C" + by Henry Rabinowitz & Chaim Schaap + pub. by Prentice Hall + ISBN 0-13-685967-4 + + "Portable C and Unix System Programming" + by J.E. Lapin + pub. Prentice-Hall + ISBN 0-13-686494-5 + +[END] -- cgit v1.2.3-54-g00ecf