All three versions can be improved.
The first function has the problem that
strlen(s) is likely to be called twice.
strlen() is an expensive operation, as it walks along the entire string until it finds the NUL terminator.
The second version (the macro) has the same issue, with the additional caveat that it breaks if
s is an expression with a side-effect, such as an assignment. For example,
MAX_SIZE_OF(str++) would expand to
(strlen(str++) > M_MAX_SIZE ? M_MAX_SIZE : strlen(str++))
and possibly lead to
str being incremented twice. (Even if you decide that the
ALL_CAPS_NAME alerts programmers to the fact it is a macro and thus susceptible to this kind of thing, it's still a trap that is better avoided altogether.)
The last version is better, in that it avoids both the second call to
strlen() and the double-argument-expansion problem. However, I still have two objections.
First, I think that you would be better off decomposing the problem into two parts, like
MIN(M_MAX_SIZE, strlen(s)). The
MIN() macro, although not in the C standard, is frequently reimplemented, and widely understood. On the other hand, it is less obvious what your
maxSizeOf() is supposed to do. I'm not sure that it's even worth the brain power to keep track of the existence of this function.
Second, if the string that is passed to the function is much longer than
M_MAX_SIZE, it still needs to walk all the way to the end of the string — even if
M_MAX_SIZE is 5 and the string is a gigabyte long. To avoid that problem, I think that you may be better off walking down
s yourself. And if you do hunt for the NUL terminator yourself, then it is suddenly worthwhile to have this function again.
More importantly, worrying about the efficiency of the function compared to the macro is a false optimization. The operation that could potentially take a long time and scale poorly is
strlen(). That's the performance problem that would bite you — not the function-vs.-macro stuff that you are worrying about.