After careful consideration and a bit of editing, I judge that it should not be embarassing, illegal, job-threatening, or otherwise dangerous for me to post the following section of the company C style guide. It does touch directly on some things that have already been discussed. It's even a bit amusing. I will decline to admit which part(s) might be directly attributed to yours truly :-) Enjoy BillW Writing [censored] Code: Style Issues : A.1.1 Coding Conventions: Something for Everyone to Protest : The foregoing rationale for coding conventions might not be enough for some readers, who might offer protestations that their personal conventions, used for much, if not most, of their careers in software engineering previous to their employment at [company], are technically superior. And some of these arguments may well be correct. But a major goal in coding is consistency of coding style and implementation. Whether you think [our] conventions are good, bad, or indifferent, the current coding style is the one we chose more than 10 years ago. Everyone who has joined the company since then has had to conform to it. Unless there is an overriding reason to change (read: "We sell more product, resulting in higher stock prices"), you will also have to conform. Because it is impossible to arrive at agreement about every coding convention issue, all engineers might find something in this document that is not to their ultimate liking. However, you are reminded that in a successful compromise, everyone feels equally shortchanged. A.3.2 Fifty Ways to Shoot Yourself in the Foot About 10 years ago, a wag in Datamation remarked, "C is a language for consenting adults, Pascal is a language for children, and Ada is a language for hardened criminals." The writer was referring to how closely the compilers for these languages herd programmers into doing things The Right Way. C allows an engineer to solve the problem at hand with a great deal of freedom and personal discretion. However, just because the compiler allows you to do something in a quick-and-dirty fashion does not imply that you should use the quick-and-dirty solution. With the freedom from constraints offered by the C language comes the responsibility not to abuse this freedom. When the language offers a more robust and structured solution to the problem at hand, choose that solution even though it might require additional effort. Although ANSI C offers much more data type checking than K&R compilers do, C is largely promiscuous about what it accepts. C does not check array boundaries, and it does not place checks on your pointer conversions or mathematics to ensure that your result is pointing to something valid. C allows you to overflow simple numeric calculations silently and with stunning efficiency. C allows you to scribble over your stack frame, which will guarantee that when you return from the function you are currently in, you will return to the Land of Oz. In short, C does not hold your hand when you need help, nor will it smack you on the wrist when you err. As such, the demands placed on the programmer are far greater in C than in some other languages. You alone are responsible for checking your intermediate results for conformance with reality before your code makes assumptions for later execution. It is to your advantage to use what is available in the ANSI C language to help you write code that does what you mean, not just what you say: Function Prototypes Use function prototypes. There is no excuse for not using them. You should define a function prototype only once in the entire source, in one header file. Defining multiple prototypes defeats the purpose of having a function prototype in the first place. Order of Functions within a File Deliberately arrange the order in which the functions are defined in a file so as to make forward declarations unnecessary and your source easier to maintain and follow. Typically, you do this by placing the function that calls most other static functions at the bottom of the source file and placing the functions at the bottom of the caller/called hierarchy towards the top of the file. Typecasting Do not use gratuitous typecasting. One of the most annoying things to observe in C code is a typecast that, with more careful attention to coding, would not be needed. The vast majority of the cases in which typecasting is used are a direct result of either ignorance ("I didn't know that I could do that without casting"), apathy ("So what? It doesn't slow anything down"), or both ("I'm both stupid and slovenly, so there!"). Functions that return void * (known as an opaque type) need no typecasting of the returned pointer. Doing so defeats the whole purpose of declaring the function with an opaque type. Obscure C Features Avoid using obscure C language features, such as the "," operator. Ensuring Correct Results Do not depend on nonobvious associativity and side effects for correct results. It is unwise to rely on the order of evaluation of side effects performed on variables passed to functions, and it is extremely unwise to use side effects in the invocation of a macro. Static Class Use the static storage class to reduce the scope of variables and functions. Do not make any variable or function an extern unless it is used outside the file in which it is defined. This not only helps to keep the name space cleaner, but it also reduces the size of the symbol table passed to the linker. Smaller symbol tables in the link step of the build mean faster link times. Const Qualifier Use const type qualifiers to allow the compiler to enforce read-only declarations of read-only global storage and pointers that should not change at run time. Without memory protection, there is no way at run-time to prevent someone from writing code that creates a pointer to a non-const variable, sets the value of this pointer to point to your storage that has been declared const, and subsequently overwrites the storage that had been defined as read only. However, using const allows the compiler to flag code that might try to write directly to what you want to be read-only storage. In addition to declaring initialized storage to be const, you should also declare and define the read-only parameters of functions to be const. Register Storage Class Do not use the register storage class unless you are sure--and have verified by looking at the generated code--that using the register declaration generates better code than not using it. GCC does not treat the register storage class as a mandate, only a "strong hint." Format of Data Structures Do not make assumptions about the layout or padding of a structure or the allocated size of a data structure. These depend on the compiler implementation and can vary significantly with the type of target CPU. Conversion from Signed to Unsigned Types Pay attention to the conversion from signed to unsigned types. This is one area where the transition from K&R C to ANSI C occasionally surprises people. In particular, implicit conversions (such as in varargs lists) will promote a uchar or ushort to int (since there will be no loss of precision). For instance, this means that a uint8 will need to be formatted as %d and not as %u. Passing Structures Do not pass large structures (structures larger than 12 bytes or so) by value to a function, especially on code targeted to RISC architectures. One of the "silent" changes between K&R C and ANSI C is that while K&R C always passes structures by reference to a function, ANSI C makes it possible to pass structures by value. Likewise, do not return structures larger than 4 bytes from a function. Instead, return a pointer to the result. Mixing C and Assembly Language GCC allows you to insert assembly language instructions directly into your C code. For reasons of readability and maintenance, we recommend that you contain all such code in as few source files as possible and do not spread such constructs widely across the source code. Floating-Point Operations While it might seem outrageous that we even need to mention it, using floating-point operations in source code is a great way to have someone else shoot you in the foot. [censored] Floating point is unnecessary. Those who offer protestations to the contrary will be flogged. Besides, the only people who like floating-point code are pipe stress freaks and crystallography weenies. A.4 Presentation of the Source Code Presentation of the source code is also known as pretty printing. Although the arrangement of white space in the code has little, if any, net effect on code operation, it affects the speed with which your fellow engineers can read and understand your code. Unless you think that unreadable code assures you some measure of job security, there is no logical reason why you would not want your fellow engineers to understand your code as clearly and quickly as possible. (Unreadable code does not lead to job security. Far from it, such code will more than likely lead to your fellow engineers' discontent.) Nonetheless, pretty-printing conventions seem to be one of the issues in software engineering that generate a most rancorous debate. Fortunately, there are tools that make maintaining a consistent pretty printing and white space very easy. Both the GNU Emacs editor and the indent utility allow for easy formatting of C code with a minimum of manual effort. Note: When updating code, "white space only" changes are not accepted in code review because they very likely will cause sync conflicts when doing branch syncs, collapses, or merges. Code can only be reformatted when a change in nesting level (such as adding an enclosing "if" statement) necessitates the addition or deletion of indentation. A.4.1 Specific Code Formatting Issues The following points address specific code formatting issues: Standard Header Each file should have a standard header. Templates for the headers of all common types of source files are in the sys/templates/header.*. #include Directives All #include directives should occur after the file header. Column Width All code must fit in 80 columns. Standard Indentation The standard indentation is four spaces. The exception is switch statements, where the case statements are kept at the same indentation level as the enclosing switch {...}. TABS happen every 8 spaces. The indenting standard is 4 spaces. Any number of tabs and spaces can be combined to yield the required amount of indentation, although it is certainly desirable that the total number of characters used be minimized (tabs first, then 8-n spaces.) Any reasonable editor and nearly all tools can do this correctly. Deal with it. The characters used for indentation should not be gratuitously changed without other edits in the same line. The major tool that looks weird when tabs+spaces and spaces-only indentation are mixed in the same source file is the output from context diffs. When the indication characters are prepended to the lines ("! ", "+ ", "- "), a line indented with a tabs first won't actually move, while one with spaces will have the entire line shifted." Diffs generated for human consumption--i.e. code review and not to be applied as patches--should be made with '-t' which instructs diff to expand all tabs into the appropriate number of spaces so that the leading '! ', etc. will not affect the columnar output and hence readability. Spaces in Function Definitions and Prototypes In the function definition, there should be a space following a function name and before the opening parenthesis of the argument list. In the prototype argument list in function declarations, there should be no space between the function name and the opening parenthesis. The following is an example of this formatting style: In the file boojums.h: extern void boojum(int, struct snark *); In the file boojums.c: void boojum (int arg1, struct snark *ptr) { /* * Do a bunch of stuff. */ } If...Else Statements The else clause of an if {...} else {...} should be "cuddled" as shown in this example: if (boojum) { /* Do some stuff here. */ } else { /* Do something else. */ } Assignments in conditional statements are strongly discouraged (except where the flow-of-control would be demonstrably more complicated without them) and should not be used in simple situations like this: if ((str = malloc(BUFSIZ)) == NULL) { return; } Spaces around Parentheses Put a space between flow control reserved words and the opening parenthesis of the control statement's test expression: Correct: if (test_variable) Incorrect: if(test_variable) The return statement should follow the same rule: Correct: return (TRUE): Incorrect: return(TRUE) There should not be a space between the name of a function and the opening parenthesis of the actual argument list: Correct: ret_value = boojum_function(arg1, arg2, arg3); Incorrect: ret_value = boojum_function (arg1, arg2, arg3); Stubbing Out code Do not use #if 0...#endif to "stub out" code. To stub out code, use an undefined preprocessor variable that gives some clue about why the code is stubbed out, with comments indicating why the code is stubbed out, by whom and when it might be used. For example: /* * This feature will be enabled in release 10.0(2) */ #ifdef TO_BE_ENABLED_IN_RELEASE_100_2 ... #endif An exception to this rule is debugging code, which you place under a conditional compilation variable DEBUG or GLOBAL_DEBUG: #ifdef DEBUG ... #endif Formatting Block Comments Format block comments as follows: /* * This is a block comment. Don't embellish these with lots of "stars * and bars." */ Switch Statements Case statements are indented at the same level as the enclosing switch. Each case should end with break, continue, return, /*FALLTHRU*/, or in special circumstances, goto. Don't put a break after any of the others, because an unreachable statement will cause compile warnings or static analysis warnings. The /*FALLTHRU*/ comment allows checking by static analysis tools. For example: switch (number) { case 0: printf("You entered zero. I assume you meant 1.\n"); /*FALLTHRU*/ case 1: { uint count; frobozz(&count); if (count > min_required) retval = count; break; } default: ... } In this switch statement: 1 . The variable count is meant to have a very short lifetime, because it is effectively a temporary variable, and its scoping reflects that. 2 . The break occurs within the scoping, as do all other statements for this case. For more information about switch statements, see section A.6 "Coding for Reliability." A.4.2 Some Comments about Comments Yes, we've all heard the refrain "comment your code" until we're all tired to death of hearing it. Well, this is a perfect place to say it again, but with some more specific points. Comments should tell the reader something non-obvious. A comment that repeats what is blindingly obvious is annoying at best. The following is an example: boojum += 10; /* Add ten to boojum */ This tells you a lot, doesn't it? It is often better to aggregate comments about high-level and architectural issues in one place, to allow the reader and maintainers of your code to learn much more in a shorter time than if they had to piece together the issues and ideas about your features from comments strewn throughout several files. A good example of aggregating comments is the large block comments in the files [censored] Keep comments up to date with the code. Comments that no longer accurately represent what the code is doing are often worse than nonexistent comments. Devote block comments to content, not fancy, exquisitely formatted "stars and bars" borders. If you're about to execute one of those stunningly elegant, minimalist representations of excessive cleverness that C allows all too easily, give the reader a clue about what the outcome of your little pearl of syntax construction should be. (But better than that, don't become yet another obfuscated C coder: rewrite your stunning little pearl.) A.5 Variable and Storage Persistence, Scope, and Naming For variables, functions, and program storage, use the minimal scoping required to get the job done. In other words, do not define a variable to be static when an auto will do the job, and do not define a variable to be extern if a static scoping will work. The naming of variables and functions must adhere to different standards according to their scope. auto variables must be unique only within the function in which they are declared. static variables and functions must be unique within their compilation unit. External variables and functions must be unique throughout the entire lot of the source code going into the link step. As such, prefix variables and functions defined with extern scoping with a well-understood and consistent prefix that identifies the module and subsystem, and use these prefixes for all extern variables and functions contained within the module or subsystem. The prefix should be at least two characters long. The following are well-known and obvious prefixes for extern variables and functions: * ip [censored somewhat] The prefix 'my' is neither well known nor obvious. Note: The use of externs in source (.c) files will cause static analysis warnings, and will later need to be fixed. _______________________________________________ http://www.piclist.com PIC/SX FAQ & list archive View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist