Macros are expanded exactly once, when the program is compiled. Since time travel is not part of the C language, it is impossible for a future execution of a program to retroactively change the consequence of a macro. So if a computation, such as computing the length of a string, depends on information not known when the program is compiled, a macro is completely useless. Unless the string happens to be a literal, this will be the case. And I venture to assert that in the vast majority of cases, the string whose length is required did not exist when the program was compiled.
A clear understanding of what macros actually do -- modify the program text before compilation -- will help avoid distractions such as the suggestion in this question.
It can occasionally be useful to use strlen
on a constant string literal, in order to avoid bugs which might be introduced in the future when the string literal is modified. For example, the following (which tests whether line
starts with the text Hello
):
/* Code smell: magic number */
if (strncmp(line, "Hello", 5) == 0) { ... }
Would be better written as:
/* Code smell: redundant repetition, see below */
if (strncmp(line, "Hello", strlen("Hello")) == 0) { ... }
Obviously, if a computation can be performed once at compile-time, it would be better to do so rather than doing it repeatedly when the program runs. Once upon a time when compilers were primitive and almost incapacable of understanding control flow, it made some sense to worry about such things, although even then a lot of the hand-optimisations were much too complicated for the minor benefits achieved.
Today, even this excuse is unavailable for the premature optimizer. Most modern C compilers are perfection capable of replacing strlen("Hello");
with the constant 5, so that the library function is never called. No macro magic is required to achieve this optimisation.
As indicated, the test in the example still has an unnecessary repetition of the prefix string. What we really want to write is:
if (startsWith(line, "Hello")) { ... }
Here the temptation to define startsWith
as a macro will be very strong, since it seems like simple compile-time substitution would suffice. This temptation should be avoided. Modern compilers are also capable of "inlining" function calls; that is, inserting the body of the call directly into the code.
So the definition:
static int startsWith(const char* line, const char* prefix) {
return strncmp(line, prefix, strlen(prefix)) == 0;
}
will be just as fast as its macro equivalent, and unlike the macro it will not lead to problems when it is called with a second argument with side effects:
/* Bad style but people do it */
if (startsWith(line, prefixes[++i])) { doAction(i); }
Once the call is inlined, the compiler can then proceed to apply other optimisations, such as the elimination of the call to strlen
in case the prefix argument is a string literal.