[size="5"]Remember The Return Types
My first example of this "template programming" will involve the formatting output function sprintf. It is not unusual to write code such as:
sprintf(str1, "Old v=%d\t",v);
/* Some code that plays with v */
sprintf(str2, "New v=%d",v);
strcat(str1, str2);
printf(str1);
Most instances of sprintf use a temporary string as the first parameter. This is the template that becomes engrained: "sprintf requires a temporary string". Whereas a better template would be a syntactical one, "sprintf requires a pointer to a character [array]". This is a reminder that we could, instead, use a function that returns a char * as our first parameter, saving a temporary buffer. For example:sprintf(str, "Old v=%d\t",v);
/* Some code that plays with v */
sprintf(strchr(str, '\0'), "New=%d",v);
printf(str);
You should be prudent in using pointers directly, to avoid deferencing a NULL. In this case, strchr(str, '\0') is always valid thanks to the first sprintf. There is also a hidden bonus with sprintf - it returns the number of characters written into the buffer - which can save a call to strlen! Check your libraries. You might get lucky!To take another example, if the reason for getting a string is to determine its length is valid for some operation, you might write:
len = strlen(GetFileName());
if (len > 0)
; /* File name is not null */
However, if all you're going to do is check len against zero, why not write:if (GetFileName()[0] != '\0')
; /* File name is not null */
This time, not only do we save ourselves another temporary buffer (which is quite common, seeing as we use the result directly - it also eliminates the extraneous copy that usually follows), but we also show understanding for the data being returned (that a NULL-length string starts with '\0'), and we also remove the strlen overhead. Note that the same result could be achieved with the equivalent code:if (*GetFileName () != '\0')
; /* File name is not null */
As a string is simply a structure (of type char *), you could also use this method with functions that return whole structures, C++ classes or pointers to them, and just isolate the element you want. For example:struct POINT GetCurrentPos(void);
int y;
y = GetCurrentPos().y;
or,printf(GetDevice()->pName);
The compiler is likely to create the structures as temporary variables, and pass their pointers as hidden parameters, so you need not worry about the overhead of copying structures to and from the stack.Just as a side point, remember the lessons that say "you can't return a string from a function"? Well. You can. Of sorts. By creating a structure of type STRING consisting of one character array called str, for instance, you can return STRING from a function, referencing it in the same manner.
struct STRING { char str[256]; };
struct STRING GetName(void)
{
.
}
printf(GetName().str);
In the real world, this method offers little or no benefit to passing the string in as a pointer, but it does save the programmer creating temporary variables.[size="5"]Expressions
Function calls are just expressions. If they return a type (excluding void, which isn't really a type) then you can use them anywhere you would use a normal expression, like a while loop. This allows entire loops and the like to be short-circuited quickly, taking advantage of C's lazy evaluator (see sidebar).
while (1 && GetNextLine(&str))
;
Changing the 1 to a 0 causes the whole expression to evaluate to 0: The GetNextLine function does not get called, and the loop never executes. I often use this method to remove long winded if statements from experimental code:if (1
&& ComplicatedExpression1
&& ComplicatedExpression2
&& ComplicatedExpression3
)
;
By making the 1 a global, or static, variable, I can then remove the code during program execution with the debugger, or change it with a special menu option, allowing me to test different section on a single compile.[size="5"]End
I hope there's a few new ideas for you there. I neglected to mention code could also be removed with
/*
. code here .
//*/
and re-inserted by adding a single / to the first line. But that's just too nasty![size="5"]Sidebar
C uses a lazy evaluator. This means it will only evaluate the necessary expressions to prove the expression is definitely TRUE, or definitely FALSE. So a line like,
if (fn1() && fn2() && fn3())
.
Would evaluate fn1() first, but would only continue onto fn2() if it had too. Namely, if fn1() returned TRUE. If it returned FALSE, there is nothing the other functions could do to make the final expression TRUE. Most people know this instinctively from code snippets like:
if (ptr && ptr->Name)
printf(ptr->Name);
This is different from Pascal.