11.1 Notes on Program Coding

(1)

Functions with Prototype Declarations

When a function is called, the prototype of the called function must be declared. If a function is called without a prototype declaration, parameters may not be received and passed correctly.

Examples 1.

The function has the float type parameter (when dbl_size=8 is specified).

void g()
{
            float a;
            ...
            	f(a);               //Converts a to double type
}
void f(float x)
{...}

 

Examples 2.

The function has signed char, (unsigned) char, (signed) short, and unsigned short type parameters passed by stack.

void h();
void g()
{
            char a,b;
            ...
            h(1,2,3,4,a,b);     // Converts a and b to int type
}
void h(int a1, int a2, int a3, int a4, char a5, char a6)
{...}

(2)

Function Declaration Containing Parameters without Type Information

When more than one function declaration (including function definition) is made for the same function, do not use both a format in which parameters and types are not specified together and a format in which parameters and types are specified together.

If both formats are used, the generated code may not process types correctly because there is a difference in how the parameters are interpreted in the caller and callee.

When the error message C5147 is displayed at compilation, this problem may have caused it. In such a case, either use only a format in which parameters and types are specified together or check the generated code to ensure that there is no problem in parameter passing.

Example

Since old_style is written in different formats, the meaning of the types of parameters d and e are different in the caller and callee. Thus, parameters are not passed correctly.

extern int old_style(int,int,int,short,short);
    /* Function declaration: Format in which parameters and types are specified
                 together */
int old_style(a,b,c,d,e)
              /* Function definition: Format in which parameters and types are not
                 specified togheer */
int a,b,c;
 short d,e;
{
    return a + b + c + d + e;
}
int result;
func()
{
    result = old_style(1,2,3,4,5);
}

 

(3)

Expressions whose Evaluation Order is not Specified by the C/C++ Language

When using an expression whose evaluation order is not specified in the C/C++ language specifications, the operation is not guaranteed in a program code whose execution results differ depending on the evaluation order.

Example

a[i]=a[++i];  // The value on the left side differs depending on whether
              // the right side of the assignment expression is evaluated first.
sub(++i, i)	 ; // The value of the second parameter differs depending on whether
              // the first parameter in the function is evaluated first.

 

(4)

Overflow Operation and Zero Division

Even if an overflow operation or floating-point zero division is performed, error messages will not be output. However, if an overflow operation is included in the operations of a single constant or between constants, error messages will be output at compilation.

Example

void main()
{
  int ia; 
  int ib; 
  float fa;
  float fb;
  ib=32767;
  fb=3.4e+38f;
  /* Compilation error messages are output when an overflow operation */
  /* is included in operations of a constant or between constants */
  ia=99999999999;	       /* (W) Detects overflow in constant operation */
  fa=3.5e+40f;          /* (E) Detects overflow in floating-point operation */
  /* No error message is output for overflow at execution */
  ib=ib+32767;          /* Ignores overflow in operation result */
  fb=fb+3.4e+38f;       /* Ignores overflow in floating-point operation result */
}

 

(5)

Writing to const Variables

Even if a variable is declared with const type, if assignment is done to a non-const type variable converted from const type or if a program compiled separately uses a parameter of a different type, the compiler cannot check the writing to a const type variable. Therefore, precautions must be taken.

Example

<Example>
const char *p;             /* Because the first parameter in library     */
:                          /* function strcat is a pointer to char, the  */
strcat(p, "abc");          /* area indicated by the parameter may change */
  file 1
const int i;
  file 2
extern int i;              /* In file 2, variable i is not declared as  */
:                          /* const, therefore writing to it in file 2  */
i=10;                      /* is not an error                           */

 

(6)

Precision of Mathematical Function Libraries

For functions acos(x) and asin(x), an error is large around x=1. Therefore, precautions must be taken. The error range is as follows:

Absolute error for acos(1.0 − ε) double precision 2-39 (ε = 2-33)

single precision 2-21 (ε = 2-19)

Absolute error for asin(1.0 − ε) double precision 2-39 (ε = 2-28)

single precision 2-21 (ε = 2-16)

 

(7)

Codes that May be Deleted by Optimization

A code continuously referencing the same variable or a code containing an expression whose result is not used may be deleted as redundant codes at optimization by the compiler. Variables should be declared with volatile in order for accesses to always be guaranteed.

Example

[1] b=a;            /* The expression in the first line may be deleted */
                    /* as redundant code                               */
    b=a;
[2] while(1)a;      /* The reference to variable a and the loop   */
                    /* statement may be deleted as redundant code */

 

(8)

Differences between C89 Operation and C99 Operation

In the C99, selection statements and repeat statements are enclosed in curly brackets { }. This causes operations to differ in the C89 and C99.

Example

<Example>
enum {a,b};
int g(void)
{
    if(!sizeof(enum{b,a}))
        return a;
    return b;
}

If the above code is compiled with -lang=c99 specified, it is interpreted as follows:

enum {a,b};
int g(void)
{
     {
         if(!sizeof(enum{b,a}))
             return a;
     }
     return b;
}

g()=0 in -lang=c becomes g()=1 in -lang=c99.

 

(9)

Operations and Type Conversions That Lead to Overflows

The result of any operation or type conversion must be within the allowed range of values for the given type (i.e. values must not overflow). If an overflow does occur, the result of the operation or type conversion may be affected by other conditions such as compiler options.

In the standard C language, the result of an operation that leads to an overflow is undefined and thus may differ according to the current conditions of compilation. Ensure that no operations in a program will lead to an overflow.

The following example illustrates this problem.

Example

Type conversion from float to unsigned short

float f = 2147483648.0f;
unsigned short ui2;
void ex1func(void)
{
	       ui2 = f;  /* Type conversion from float to unsigned short */
}

The value of ui2, which is acquired as the result of executing ex1func, depends on whether –fpu or –nofpu has been specified.

-fpu (with the FPU): ui2 = 65535
-nofpu (without the FPU): ui2 = 0

This is because the method of type conversion from float to unsigned short differs according to whether –fpu or –nofpu has been specified.

 

(10)

Symbols That Contain Two or More Underscores (__)

Symbols must not contain sequences of two or more underscores. Even though the code generated in such cases seems normal, the symbol names may be mistaken as different C++ function names when they are output as linkage-map information.

Example

int sample__Fc(void) { return 0; }

This will be output to the linkage map as sample(char) rather than _sample__Fc.