syque.com

The Psychology of Quality and More

| Menu | Books | Share | Search | Settings |

C Style: Standards and Guidelines (contents)

CHAPTER 8 : Language Usage

PART 4 : USAGE

CHAPTER 8 : Language Usage
8.1 General principles of language usage
8.2 Using expressions
8.3 Using 'if'
8.4 Using 'while'
8.5 Using 'for'
8.6 Using 'do'
8.7 Using 'switch'
8.8 Using 'goto'
8.9 Using 'continue' and 'break'
8.10 Using 'return'
8.11 Using functions
8.12 Using '#define'
8.13 Conditional compilation
8.14 Other preprocessor commands
8.15 Summary

<--Prev page | Next page -->

 

8.11  Using functions

Function calls can be used to significantly simplify code, enabling it to be layered, hiding unnecessary process information below the current level. Parameters are always passed by value; to pass by reference requires the use of a pointer (note that it is safer to always pass by value unless the item referenced is to be changed).

8.11.1  Declaring function type

It is being explicit to always declare the type of a function, even when an int or nothing is being returned:

 

struct WINDOW *
FindNextWindow()

int
ReadEmpName( char *EmpName )

void
WriteMsg( char *Msg )

 

‘void’ is standard in ANSI C, but not in original K&R, in which case it may be typedef'd.

8.11.2  Similar parameters in similar functions

When there is a group of functions which perform similar sorts of operation, it is being consistent to use the same type of parameter in the same order in each function. Even the same local names may be used:

 

ConvertHPWordToDCA( FILE *FromDoc, FILE *ToDoc );

ConvertDCAToHPWord( FILE *FromDoc, FILE *ToDoc );

 

This principle can also be used quite different functions, with general rules such as 'inputs before outputs' and 'control parameters last' being used. Note that different names should be used for different operations (although parts of names may be similar):

 

ClipHorizontal( int MinY, int MaxY );

RandomNumber( int MinValue, int MaxValue );

 

8.11.3  Maintaining parameter type

When actual parameters are passed to a function, the function does not know the type of the parameters. It treats the parameter area (usually the stack) according to the formal parameters it is given. This allows all kinds of tricks to be played, some quite common, and some quite obscure:

Arrays

A common usage of parameters is to interpret the address of an array element as a pointer:

 

char    CurrEmpName[NAME_LEN];
...
ValidateEmployee( CurrEmpName );
...
int ValidateEmployee( char *EmpName );
...

 

This is a common trick, explicitly showing what actually happens and helping terse code to be written. On the other hand, it is being more consistent to always use arrays as arrays - this will be reflected in the function formal parameter. The actual parameter can also be made explicit by passing the address of the first element of the array, rather than just its name:

 

...
ValidateEmployee( &CurrEmpName[0] );
...
int Validate Employee( char CurrEmpName[NAME_LEN] );
...

 

This can also be done the other way around, treating a pointer as the address of an array, although this is less common, and can result in string constants being (illegally) changed.

It is consistent and explicit to always maintain the original type of arrays and pointers in actual and formal parameters.

Numbers

Parameters declared as char or short are implicitly promoted to int. Similarly float is promoted to double. This allows a fair degree of flexibility in the types that a function can safely cope with. Nevertheless, it is using the explicitness principle to use the appropriate type, casting if necessary, to ensure that actual and formal parameters are visibly of the same type.

Pointers

A pointer simply points at memory, and a pointer of one type may be interpreted as a pointer of another type:

 

struct NODE
{
    char        NodeName[NAME_LEN];
    struct NODE *NextNode;
}   *CurrNode;
...
CheckName( CurrNode );  /* CurrNode also points to a character! */

int CheckName( char *NameToCheck );
...

 

Reinterpreting pointers can be implementation-dependent and is always liable to cause problems. Where actual parameters are pointers of different type to formal parameters, they should be cast to the formal parameter type.

Implementation-dependent tricks

There is another class of usage which clearly deserves the title of 'tricky'. For example:

 

unsigned int    Top16Bits;
unsigned int    Bottom16Bits;
...
ConvertToLong( Top16Bits, Bottom16Bits );
...
long ConvertToLong( long Full32Bits )    /* VERY implementation-dependent */
    return ( Full32Bits );

-----------------------------------------------------------------------------

Implementation-dependent tricks such as this are hazardous and should be avoided altogether.

Maintain type between actual and formal parameters

A simple rule that can be used to address the above problems is that the type of actual and formal parameters should always match, using casts where necessary (see 8.2.7).

If ANSI function prototypes are used, this will enable the compiler to be used to detect difference in types between formal and actual parameters.

8.11.4  Passing large amounts of information

Sometimes it is necessary to pass a function a significant amount of data. There are two common ways of doing this:

(a)  Pass all items as multiple separate parameters

Using a large number of parameters is unwieldy and can be difficult to read and absorb.

What is a large number of parameters? A good answer is, "Where the whole function call cannot be absorbed by the reader as a single item", which suggests around seven as a reasonable maximum (see 2.9). A function call also becomes more difficult to read when its parameters wrap to the following line.

(b)  Pass the items grouped in data structures

Passing data struct's which contains all or most of the items required by the called function is a powerful way of simplifying the interface, although if the structure is not cohesive (ie. the data in it is not clearly related), it can also reduce the understandability of the call.

Passing a large structure where only a few of its elements are to be accessed is hazardous, as it exposes the unused 'tramp data' to corruption (see 9.5.3).

Also, passing struct's by value is not completely portable, and is inefficient if they are large, although it does protect the data from change. It is more normal to pass pointers to struct's.

(c)  Use a common data area

Using global data is seldom a good idea, and should be avoided (see 9.4).

 

Treating the symptoms by hiding the data in structures may not always be the best solution. It is often better to investigate the design of the function itself, which may turn out to be performing more than one function!

8.11.5  Complex parameters

It can be quite concise to include expressions in the actual parameters of a function call:

 

PlotPoint( MouseX + PrevMouseX * XFactor, MouseY + PrevMouseY * YFactor);

 

This is a form of nesting, where the reader must push the meaning of the called function onto his mind-stack whilst he works out the meaning of the parameters. The alternative is to calculate the parameters separately before calling the function:

 

NewX = MouseX + PrevMouseX * XFactor;
NewY = MouseY + PrevMouseY * YFactor;
PlotPoint( NewX, NewY );

 

Another hazard is in the use of function calls in parameters:

 

PlotPoint( TrajectoryX(MouseX, PrevMouseX), TrajectoryY(MouseY, PrevMouseY);

 

The danger here (aside from the complexity issue) is that error returns cannot be handled. Assuming that it will never return an error is very hazardous.

8.11.6  Variable numbers of parameters

Functions such as 'printf', which use a variable number of actual parameters are easy to write in a non-portable manner, and can be messy in declaration and use. If possible, they should be avoided. Where they are necessary, care must be taken to use only defined methods, such as with varargs.h (non-ANSI) or stdarg.h (ANSI).

 

<--Prev page | Next page -->

 

Site Menu

| Home | Top | Settings |

Quality: | Quality Toolbook | Tools of the Trade | Improvement Encyclopedia | Quality Articles | Being Creative | Being Persuasive |

And: | C Style (Book) | Stories | Articles | Bookstore | My Photos | About | Contact |

Settings: | Computer layout | Mobile layout | Small font | Medium font | Large font | Translate |

 

You can buy books here

More Kindle books:

And the big
paperback book


Look inside

 

Please help and share:

 

| Home | Top | Menu |

© Changing Works 2002-
Massive Content -- Maximum Speed