2.5.3  Programming and Source Code Standards (Cont. 1) 
C Programming 
Source Code Files  (01-01-2004)
Global Definitions

  1. Place the global (external) data declarations after the Defines/ Typedefs.

  2. Use the order: externs, non-static globals, static globals

  3. If a set of defines applies to a particular piece of global data (such as a flags word), place the defines immediately after the data declaration or embedded in structure declarations, indented to put the defines one level deeper than the first keyword of the declaration to which they apply.  (01-01-2004)
Function Placement

  1. Place the functions last.

  2. Place like functions together.

  3. Use a "breadth-first" approach (functions on a similar level of abstraction together) rather than depth-first (functions defined as soon as possible before or after their calls).

  4. If defining large numbers of essentially independent utility functions, consider alphabetical order.  (01-01-2004)
Other Files

  1. For operator-directed programs, establish a file called " Readme" to document both the file and issues for the program or a group of programs. For example, it is common to include a list of all conditional compilation flags and what they mean.

  2. List files that are machine dependent, etc.  (01-01-2004)
Global Variable Declarations

  1. The following subsections address global variable and structure declarations.  (01-01-2004)
Global Variables

  1. Avoid the use of global variables unless you have cases where the use of global variables can actually make a program more readable by not cluttering function calls.

  2. Declare any global variables at the top of a file, before any function declarations.

  3. Declare variables, which are global to only the functions in a single file, as "static" .

  4. Use meaningful names (MaxLength=30 characters).

  5. Separate the words of a compound variable by capitalizing the first letter of every word.

  6. Start pointer names with "p" .

  7. Separate unrelated declarations, even of the same type, on separate lines.

  8. Tab the names, values, and comments so that they line up. See the following table.

    int EventInit; /* event_init performed */
    char TaxFormType; /* type of the tax return form /
    char *pFirstEntry; /* ptr to 1st entry */  (01-01-2004)
Structure Declaration

  1. Declare each field in a structure on a separate line.

  2. If you declare a local structure – use lower case for the names. If you declare a global structure – use mixed case for the names. The variables that comprise the structure (structure elements) must follow the same rule as local or global variables

  3. Assign a structure to a variable in a separate statement.

  4. Recommended Styles:

    1. The following table illustrates a style where the opening brace ({) should be in column 1 on a next line after the structure tag, and the closing brace (}) should be in column 1.

    struct ECT_REG_HEADER_S
    char *pFirstEntry; /* ptr to 1st registry entry */
    int NumEntries; /* number of entries */

    2) The following table illustrates a style where the opening brace ({) should be on the same line as the structure tag, and the closing brace (}) should be in column 1. Choose one of these styles for the opening brace ({) and consistently use it.

    struct ECT_REG_HEADER_S {
    char *pFirstEntry; /* ptr to 1st registry entry */
    int NumEntries; /* number of entries */
    }  (01-01-2004)
Typedef Declaration

  1. Structures may be typedeffed when they are declared.  (01-01-2004)
Local Variable Declarations

  1. Do not use names with leading and trailing underscores for any user-created names as they are reserved for application system purposes. Most application systems use them for names that the user should not have to know.  (01-01-2004)
Local Variable Names

  1. The following paragraphs cite guidelines.

  2. Declare local variables at the start of, or just before, the block in which they are used. If the variable name is going to be reused in a different block in the same function then declare the variable at the start of the function.

  3. Do not have a function contain two variables with the same name.

  4. Avoid declaring variables within any block, (e.g., within a " for" block).

  5. Use meaningful names (max_length=30 characters).

  6. Begin all variable names with a lowercase letter.

  7. Place the pointer qualifier, * with the variable name rather than with the type.

  8. Separate unrelated declarations, even of the same type, on separate lines.

  9. Include a comment describing the variable in the same line.

  10. Tab the names, values, and comments so that they line up. An example follows.

    int event_init; /* event_init performed */
    Char tax_form_type; /* type of the tax return form */
    Char *first_entry_p; /* ptr to 1st entry */  (01-01-2004)
Typedef Declaration

  1. Give the struct and typedef the same name. Apply the same rules as for a structure declaration.  (01-01-2004)
Abbreviations for Common Variable

  1. Use conventional abbreviations for common variables.

    average avg
    database db
    Length len
    message msg
    number num
    position pos
    String str  (01-01-2004)

  1. This subsection addresses constants and enumeration data.  (01-01-2004)
Defining Constants

  1. Avoid coding numerical constants directly. But, a numerical constant that affects and is used throughout the whole program should be declared to facilitate changes, but actual numbers could be used in small scope code.

  2. Use symbolic constants to make the code easier to read.

  3. Define the value in one place to make it easier to administer large programs since the constant value can be changed uniformly by changing only the define.  (01-01-2004)
Consistency of Constant Definitions

  1. Consistently define constants with their use, (e.g. use 540.0 for a double instead of 540 with an implicit float cast).  (06-01-2004)
Conventional Constants

  1. Use a conventional set of symbolic constants for constants common to C coding:

    TRUE commonly defined by system supplier headers. Do not redefine.
    FALSE commonly defined by system supplier headers. Do not redefine.
    #define FALSE 0 commonly defined by system supplier headers. Do not redefine.
    #define TRUE !FALSE commonly defined by system supplier headers. Do not redefine.
    LF_CHAR line feed character
    CR_CHAR carriage return character
    EOS end of string character
    EOF commonly defined by system supplier headers. Do not redefine.  (01-01-2004)
Enumeration Data

  1. Use the enumeration data type to declare variables that take on only a discrete set of values, since additional type checking is often available.

  2. Declare each field in a enum on a separate line.

  3. Ensure the enum type name has a tag, in upper case with " _E" appended to their name

  4. Ensure each field in an enum type is in upper case separated by underscores.

    enum ACMW_E
    }  (01-01-2004)
Symbolic Constants - #define

  1. Name all quantities that must remain unchanged throughout a program using the "#define" capability. The defined name must be in upper case letters.

    #define AT_AUDIT_NAME "7"  (01-01-2004)

  1. This subsection addresses:

    1. return values

    2. parameter lists

    3. function body

    4. function prototype

    5. function naming  (01-01-2004)
Return Values

  1. Explicitly declare all return values.

  2. Do not default to int; if the function does not return a value then give it return type void.

  3. If the value returned requires a long explanation, give it in the prologue.  (01-01-2004)
Parameter Lists

  1. If the function and its parameter list is longer than one line, indent lines after the first one from the left margin so that the second line of the parameter list starts directly below where the parameter list begins on the first line.

  2. Ensure a function that returns information via one or more of its parameters only returns status information in its name.

  3. Ensure each parameter passed to a function occurs on a separate line in the function prologue with a short comment describing its function.  (01-01-2004)
Function Body

  1. Tab all local variable declarations and code within the body over one stop.

  2. Place the opening brace, "{" , opening the body of the function on a line by itself and left justified or at the end of the line which introduces the block. Any control statements will cause further indentation from this basic indentation.

  3. If the function uses any external variables (or functions) that are not declared globally in the file, provide the declarations in the function body using the extern keyword.

  4. Avoid local declarations that override declarations at higher levels.

  5. Redeclare local variables in nested blocks.

  6. Ensure that a single return statement is always present with a parameter if the function is not of type void. Use one even if the function has no return value and, therefore the C language does not require a return. This can be useful for setting a break point during debugging.

  7. Place a closing brace, "}" , closing the body of the function, on a line by itself and left justified.  (01-01-2004)
Function Prototype

  1. Generate Function Prototype for all functions generated.  (01-01-2004)
Function Naming

  1. Select a naming convention (capitalization, underscores, etc.) and use it consistently.

  2. For naming services:

    • Limit service name to 12 characters. Longer names, when accepted, by Tuxedo, will be truncated to 12 characters.

    • Begin all service names with an uppercase letter.

    • Separate the words of compound service names by capitalizing the first letter of every word.  (01-01-2004)

  1. Use comments in your programs to meet the following three (3) goals:

    1. Clear and concise

    2. Place where needed

    3. Useful to read the code

  2. Strive to balance the amount of comments with the amount of whitespace to maintain readability and clarity.

  3. Make sure the comments describe what is happening, how it is being done, what parameters mean, which globals are used and which are modified, and any restrictions or bugs.

  4. Avoid comments that are clear from the code. Such information rapidly gets out of date, is redundant and clutters the code.  (01-01-2004)
Template for File and Header

  1. Place the following header at the beginning of the file:

    // Internal Revenue Service  
    // For Official Use Only  
    // Filename: Filename
    // Description: Describe the purpose of the objects in the file,
    // followed, in the case of source files, by a list of
    // functions whose definitions appear in the file
    // Related Files: An identification of any routines or files that this
    // file may require
    // Restrictions/ Known special cases where the file may not work
    // Problems:  
    // Date Modified: Date: YYYY/MM/DD
    // Version id: Revision:
    // Author: Author: <First Name> <Last Name>
    // Locked by: $Locker: $
    // Revision History: To clearly identify all the changes, when doing code reviews, print out the information. Look at ClearCase to identify the revision date. If enough information is there, look at the header for specifics. Not putting it in the code reduces your options to hoping the clearcase information is sufficient, and doing a line-by-line review in clearcase until you find the change.

  2. Place the following header at the beginning of the function:

    * Function Name:  
    * Description: A description of the major task(s) performed by
    * routine. It should be a series of one or more simple
    * verb/object statements
    * Input parameters:  
    * Output parameters  
    ********************************************************************/  (01-01-2004)
Function Comments

  1. Place comments that describe data structures, algorithms, etc., in block comment form.

  2. Present code in paragraph form prior to a block of code.

  3. Use comments for cohesive blocks of code when they explain the purpose of the block in accomplishing a cohesive task. This enables the reader to understand the function being implemented. Thus the reader will be able to quickly find the appropriate section of code without getting bogged down in coding details for other sections of code. For example:

    * cleanup_pipeline PROCESSING *

  4. Indent block comments to the same level as the block being described.

  5. Indent one-line comments alone on a line to the tab setting of the code that follows. For example:

    if (argc > 1)
      /* Get input file from command line. */
      if (freopen(argv[1], "r " , stdin) == NULL)
        perror (argv[1]);

  6. Very short comments may appear on the same line as the code they describe, but must be tabbed over to separate them from the statements.

  7. If more than one short comment appears in a block of code they must be tabbed to the same tab setting. For example:

    if (a == EXCEPTION)
      b = TRUE; /* special case */
      b = isprime(a); /* works only for odd a */
    }  (01-01-2004)

  1. This subsection addresses the following topics related to statements:

    1. Statements per Line

    2. Single Statement Blocks

    3. Multiple Statement Blocks

    4. Levels of Control Structure Nesting

    5. Goto Statement

    6. Break Statement

    7. Null Statement

    8. Conditional Statement

    9. Exit Statement

    10. Default Truth Value

    11. Increment and Decrement Operators

    12. Added Statements for Debugging  (01-01-2004)
Statements per Line

  1. Generally, source code should depict one statement per line.  (01-01-2004)
Single Statement Blocks

  1. Block off even a single statement following a "while" , "if" , "else" , etc.

  2. Using the curly braces "{}" is required only when there is a block of more than one statement. However, putting in the braces makes the scope of the control statement very clear and helps to protect the code in the event that a second line is added to the block if the single line contains a macro, which translates into more than one line of code.

    if (SomeCondition == TRUE)
      ThisVariable = SomeVariable;
    }  (01-01-2004)
Multiple Statement Blocks

  1. Statements that affect a block of code (i.e., more than one statement) must either have the opening brace "{" at the end of the line containing the control statement, or the opening brace must be on the line immediately below and lined up with the first letter of the control statement.

  2. Indent the body of the block one step from the control statement.

  3. Place the ending brace, "}" on a line by itself and at the same indentation level as the control statement.

  4. Choose one of the two styles and use it consistently:

    1st Style:
      If (condition)
        if (condition)
      for (loop control expressions)
      while (condition)
      switch (expression)
        case constant1;
        case constant2;

    2nd Style:
      if (condition) {
      } else if (condition) {
      for (loop control expressions) {
      while *condition) {
      switch (expression) {
          case constant1;
          case constant2;
      }  (01-01-2004)
Levels of Control Structure Nesting

  1. Do not nest conditional statements, such as "while" , "if" , "else" , more than 4 levels. If more levels are required, consider using a function at one of the higher levels.  (01-01-2004)
Goto Statement

  1. Do not use the Goto statement.  (01-01-2004)
Break Statement

  1. If a particular case in a switch statement is meant to drop through to the next case (i.e., it has the same effect), the fact that the earlier case has no "break" statement must be explicitly noted with a comment. For example:

      case P1040 : /* same action as for 1065; no break */
      case P1065:
      default : /* if not 1040 or 1065, no action taken */
    }  (01-01-2004)
Null Statement

  1. Generally, Null statements should include a comment line.

  2. Place the null body of a "for" or " while" loop alone on a line and commented so that it is clear that the null body is intentional and not missing code.

    while (*dest++ = *src++)
      ; /* VOID */  (01-01-2004)
Conditional Statement

  1. Break out the function call onto a separate line followed by a new line containing the conditional statement. Often a program will branch based on the success or failure of a function call. Consider the following excerpts of source code, the first excerpt is easier to understand than the second excerpt.

    pFileHandle = fopen("some_file " , READ_ONLY);
    if (pFileHandle == NULL)
      printf("Could not open file; program terminating." );

    if ((pFileHandle = open(" some_file" , READ_ONLY))== NULL)
      printf("Could not open file; program terminating." );

  2. The preceding "fopen" example demonstrates a style, which helps to avoid internal side effects. "Side effect" as used in this example refers to the fact that the reader may focus on the "if (xxx == NULL)" aspect of the statement and not fully realize that there is a call to "fopen()" . The other danger of this type of compound statement is the potential for completely changing the meaning if the parentheses around the"pFileHandle = open()" part are left off.  (01-01-2004)
Exit Statement

  1. Except for use in error-handling functions, avoid explicit use of the "exit();" statement.  (01-01-2004)
Default Truth Value

  1. Use explicit comparison even if the comparison value will never change.  (01-01-2004)
Added Statments for Debugging

  1. If you include statements to print out information during debugging, use preprocessor switch(es) in the makefile to allow compile-time control of the debug output, and use #ifdef statements to control inclusion of the debug statements. For example:

    #ifdef DEBUG_1
    printf(some debug statement);
    Fprint(<pointer to FML buffer>);
    #end-if  (01-01-2004)

  1. Do not use the ternary conditional operator, "?:" in the main program, primarily to make the code more readable. It can still be used in macros.

  2. An increment or decrement operation should be explicitly placed in a separate statement so it would be clear what is occurring and when.

  3. Ensure that all operators which take two parameters have a single space on either side of the operator. This makes it very handy to use an editor to search for a variable assignment; you need only search for " a =" and not "a =" as well as "a= " . It also makes the code more readable.

  4. In contrast to binary operators, ensure that all unary operators (e.g., a minus sign or the address operator, "&" ) have no space between the operator and the object.

  5. Where operator precedence must be known to determine the meaning of an expression, use parentheses to eliminate any ambiguity, which might arise from lack of knowledge of operator precedence. For example, to increment the variable pointed to by the pointer "pNumTimes" , use "(*pNumTimes)++" . This use of parentheses makes it clear that the contents of location "pNumTimes" is being incremented and not the address itself.

  6. The ternary conditional operator may be useful in parameter lists and as a return value.  (01-01-2004)

  1. This subsection addresses:

    1. Database Error Checks

    2. Operations

    3. Performance

    4. SQL Statements  (01-01-2004)
Database Error Checks

  1. Ensure that all calls to the database have error checking.

  2. If a cursor is used, place an error check immediately after the declare, open, all fetches, and close cursor.

  3. If a data retrieval error occurs, end the data retrieval processing and the variable that holds the unretrieved data will be populated with the default value for missing data as per the application program guidelines. A NULL value is not acceptable.  (01-01-2004)

  1. Due to the constraint that not more than one database can be open at a time, always check to see if a database is open before there is a call to open one.

  2. Before any program exits, close the open database.

  3. Declare cursors in the same function they are used.

  4. Close and free cursors after they are used.

  5. If a SQL error occurs, log the message to display the sql code, the function name, the file name, and the error message.  (01-01-2004)

  1. Minimize overhead processing and optimize memory allocations.

  2. Do not pass more than one variable or structure to an ESQL/C function.

  3. If more than one data value needs to be populated, then create an array. Pass the pointer of the array to the ESQL/C function.

  4. If more than one variable needs to be populated, then create a structure or a linked list and pass the structure or linked list pointer to the ESQL/C function.

  5. Two calls must be made to retrieve data for arrays, structures, or linked lists. In the first call, there must be an ESQL/C function call that returns the number of values to be collected. The calling function will then allocate memory in the array or structure to contain the desired data.

  6. In the second call, the data must be populated into the array, structure, or linked list.  (01-01-2004)
SQL Statements

  1. Test SQL statements individually before embedding them in C code. This approach will increase the likelihood that the embedded code will work when properly incorporating the tested SQL statements. This should be a fast risk-reducing activity and a great enhancement.

  2. Capitalize all reserved words (i.e. SELECT, UPDATE, INSERT, etc.).

  3. Use ORDER BY only when absolutely needed – if the calling program does not require sorted data, do not use ORDER BY.

  4. For negative nested subqueries (selecting rows from a table where some condition in another table is not true), experiment with both the NOT EXISTS and NOT IN constructs to determine which is faster in your situation.

  5. Avoid usage of "OR" .

  6. For discrete lists of values, use the "IN" operator,( e.g. "WHERE city in (‘New York’, ‘Sydney’)) " instead of "WHERE city = ‘New York’ OR city = ‘Sydney’" .

  7. The SELECT statement follows these rules:

    • Keywords are left justified.

    • Alias table names will be used to prefix every selected column.

    • In the WHERE clause, list join expressions before restrictions.

  8. Where the select statement does not fit on a single line, align the columns from the various lines as in the following excerpt of code.

    SELECT e.emp_id, e.emp_nm, e.city_nm, d.dept_nm
    FROM emp e, dept d
    WHERE e.dept_id = d.dept_id;

  9. The UPDATE statement follows these rules:

    • Keywords are left justified.

    • First line is "UPDATE tablename" .

    • Next lines specify updated columns and their values.

    • Final lines are the WHERE clause.

  10. The following excerpt of source code exhibits the above rules.

    UPDATE emp
    SET job_desc = \//,
    City_nm = 'Sterling'
    WHERE emp_nm = 'JOE';

  11. The DELETE statement follows these rules:

    • Keywords are left justified.

    • First line is "DELETE FROM tablename" .

  12. The following excerpt of source code exhibits the above rules.

    WHERE city_nm = 'Sterling';

  13. User parentheses rather than the implicit SQL precedence rules for logical operators in a WHERE clause. Consider the following excerpts of source code, the first excerpt is incorrect, the second excerpt is correct.

    FROM emp
    WHERE job_desc LIKE 'widget%'
    AND name LIKE 'J%'
    OR city = 'Sterling';

    FROM emp
    WHERE (job_desc LIKE 'widget%' AND name LIKE 'J%')
    OR city = 'Sterling';

  14. For further information refer to the Enterprise Standards Profile (ESP) Attachment 1 Enterprise Data Standards and Guidelines.  (01-01-2004)

  1. Use vertical and horizontal whitespace judiciously to make the program more readable.

  2. Ensure that indentation and spacing reflect the block structure of the code.  (01-01-2004)
Vertical Spacing of Conditional Operators on Separate Lines

  1. Split a long string of conditional operators onto separate lines. For example consider the following excerpt.

    if (foo->next == NULL
      && total_count < needed
      && needed <= MAXLLOT
      && ServerActive(current -input))

  2. Similarly, split elaborate "for" loops onto different lines.

    for (curr = *listp, trail = pList;
      curr != NULL;
      trail = &(curr->next), curr = curr->next) {
    }  (01-01-2004)
Spacing for Parentheses

  1. Do not separate keywords that are followed by expressions in parentheses from the left parenthesis.

  2. Put blanks after commas in argument lists to help separate the arguments visually.  (01-01-2004)

  1. Here, "portable" means that a source file can be compiled and executed on different machines with the only change being the inclusion of possibly different header files and the use of different compiler flags. The header files will contain #defines and typedefs that may vary from machine to machine.

  2. In general, a new "machine" is different hardware, a different operating system, a different compiler, or any combination of these.

    • Be aware that the size of different data types may vary from platform to platform.

    • Be especially careful to avoid making assumptions about integers and pointers.

    • Be aware that the precision and storage format of floating point numbers may vary from platform to platform.

    • Do not assume that software will always be executed on the machine for which it is originally designed.  (01-01-2004)
Machine-Dependent Code Placement

  1. Place all machine-dependent code in a separate file from all machine-independent code.

  2. Machine-dependent code must be #ifdef'ed so that an informative error message will result if the code is compiled on a machine other than for which it is designed.  (01-01-2004)
Machine-Dependent Code Usage

  1. Only write machine-dependent code when necessary.

  2. Even if, for example, a particular piece of hardware requires that a machine-dependent routine be written, try to write any routines that support the machine-dependent code machine-independent.  (04-28-2006)
C++ Programming

  1. This subsection provides establishes controls to ensure coding of C++ programs are reliable, maintainable and portable whether developed by IRS or outside vendors.  (07-01-2006)

  1. These standards apply to all C++ programming for any project however, these standards and guidelines do not apply to source code that is generated by a tool (such as a Graphical User Interface (GUI) builder), or purchased as pre-existing software from a third party.  (04-28-2006)

  1. Classes are a very significant part of your C++ programs. Classes are where most of the processing in your programs occurs. Classes are also one of the C++ constructs where you have choices such as different types of constructors and destructors or different types of operators and assignments. The following standards will help you in writing good C++ class code.  (04-28-2006)
Class Declaration

  1. All classes shall declare an assignment operator.

  2. Headers files shall not contain more than one class definition.

  3. Header files shall not declare variables other than class member data.

  4. Class names shall be unique irrespective of case in any namespace including the IRS global namespace.

  5. Class member data shall have a trailing"_" appended to their variable name, to distinguish them from local variables within member functions. No other variables shall be named with a trailing underscore.

  6. Class declarations shall be made using the following order:

    1. Friend declarations

    2. Public members

    3. Protected members

    4. Private members

    Example: class C // correct access order { public: // ... protected: // ... private: // ... };

  7. Within the public, protected and private sections of the class declaration the following order shall be followed:

    1. Type declarations

    2. Data members

    3. Constructors

    4. Destructors

    5. Mutators

    6. Accessors

  8. The name of the class definition header file must match the name of the class implementation file.  (04-28-2006)
Constructors and Destructors

  1. All classes shall declare a copy constructor if dynamic memory allocation is involved.

  2. Classes that are meant to be instantiated only by their subclasses shall have their constructor(s) and destructor declared protected.

  3. Constructors other than the copy constructor that have only one parameter shall be declared explicitly.

  4. Classes with virtual functions and Classes with children shall define a virtual destructor.  (04-28-2006)
Class Data Initialization

  1. Data members shall be initialized in the order in which they are declared.

  2. Never declare non-const public data variables. Classes shall declare member data private or protected. Always access non-const data through public access methods.

More Internal Revenue Manual