-1

Noob alert:

for learning purposes i've been given the task to re implement the strlen() function, I've gotten the notion that this would be best done with a function like macro rather than a function, my reasoning would be that using a macro i wouldn't have to deal with passing a string to a function.

what are your thoughts? is it better to create a proper function or a macro in this case?

NOOBAF
  • 125
  • 1
  • 2
  • 10
  • As you wish. Make function first then implement as macro. There are function-like macros in C that can accept arguments. – purec Jun 02 '18 at 12:13
  • 1
    Possible duplicate of [Macro vs Function in C](https://stackoverflow.com/questions/9104568/macro-vs-function-in-c) – 001 Jun 02 '18 at 12:17
  • 1
    Function-like macros are generally a bad idea. And passing functions to strings is trivial, no problem at all. Make it a regular function. (It's *returning* strings from functions that can be tricky, but of course `strlen` doesn't have to do that.) – Steve Summit Jun 02 '18 at 12:29
  • From what I've been able to understand, using a macro I could make use of the "sizeof" operator but in a function "sizeof" wouldn't work as it would return the size of a pointer, is that correct? – NOOBAF Jun 02 '18 at 12:44
  • Don't use sizeof, just count characters. – purec Jun 02 '18 at 12:49
  • @NOOBAF That would only work for arrays declared in the same scope (or global). It won't work with pointers in macros or functions. Also won't work for arrays passed as parameters. – 001 Jun 02 '18 at 12:51
  • @JohnnyMopp so could that be considered an advantage of using macros? – NOOBAF Jun 02 '18 at 12:59
  • Yes. char buf[500]; strcpy(buf, "hello"); sizeof(buf); – purec Jun 02 '18 at 13:11
  • 1
    It seems as though you still want to use a macro, despite the advice not to. – Weather Vane Jun 02 '18 at 13:45
  • `sizeof` is of no use in any way. If the string occupies 4 bytes of a 10 byte array, `sizeof` has no value in showing the length of the string. – Weather Vane Jun 02 '18 at 13:54

1 Answers1

0

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.

rici
  • 234,347
  • 28
  • 237
  • 341