The Psychology of Quality and More |
CHAPTER 8 : Language Usage
8.11 Using functionsFunction 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 typeIt is being explicit to always declare the type of a function, even when an int or nothing is being returned:
struct WINDOW *
‘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 functionsWhen 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 );
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 );
8.11.3 Maintaining parameter typeWhen 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: ArraysA common usage of parameters is to interpret the address of an array element as a pointer:
char CurrEmpName[NAME_LEN];
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:
...
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. NumbersParameters 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. PointersA pointer simply points at memory, and a pointer of one type may be interpreted as a pointer of another type:
struct NODE
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 tricksThere is another class of usage which clearly deserves the title of 'tricky'. For example:
unsigned int Top16Bits; ----------------------------------------------------------------------------- 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 informationSometimes 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 parametersUsing 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 structuresPassing 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 areaUsing 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 parametersIt 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;
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 parametersFunctions 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).
|
Site Menu |
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 |
And the big |