Assuming all the ">" marks survived all the followups...
Yes, some compilers can do that optimization.
It is at least somewhat rare though. There are certainly some
popular compilers (such as gcc) that "know all about strlen", but
to pull a strlen() call out of a loop, they must also prove to
themselves that the memory area being strlen()-ed does not change
during the execution of that loop.
If you have done an '#include <string.h>' previously the compiler is
allowed to assume that it is the standard strlen() which is used, and
the compiler does know what it does.
Indeed, the compiler is allowed (but not required) to know this
even if you have *not* included <string.h>, as long as you have not
also overridden the name. Programmers are not allowed to reuse that
name as an external-linkage identifier.
One of the interesting things that happens with gcc is that strlen()
is expanded inline on the x86, so that if you *try* to write your
own strlen(), it may never get called:
% cat t.c
#include <string.h>
size_t f(const char *s) { return strlen(s); }
% cat u.c
#include <stdlib.h>
#include <unistd.h> /* POSIX ONLY! */
size_t strlen(const char *s) {
const char *t;
write(1, "strlen called\n", 14); /* POSIX */
for (t = s; *t; t++)
continue;
return t - s;
}
% cat main.c
#include <stdio.h>
extern size_t f(const char *);
int main(void) {
size_t t = f("hello");
printf("f returned %d\n", (int)t);
return 0;
}
% cc -O t.c u.c main.c -W -Wall
% ./a.out
f returned 5
%
What happened to the write() call? A peek at the assembly code
for t.c gives the answer: there is no call to strlen(); instead,
there is just a "repnz scasb" instruction sequence.
What happens if you take out the "#include <string.h>"? Well, you
get an error on "size_t", but if you replace it with some other
header that defines size_t, gcc still replaces the strlen() with
a "repnz scasb". Is gcc in error? No: it is allowed to do this.
On the other hand, if you replace the #include line in t.c with
the entire contents of u.c, making the local strlen() "static":
% cat t.c
#include <stdlib.h>
#include <unistd.h> /* POSIX ONLY! */
static size_t strlen(const char *s) {
const char *t;
write(1, "strlen called\n", 14); /* POSIX */
for (t = s; *t; t++)
continue;
return t - s;
}
size_t f(const char *s) { return strlen(s); }
%
then compile main.c and this new t.c:
% cc -O t.c main.c -W -Wall
% ./a.out
strlen called
f returned 5
%
*now* your function gets called. (In fact, gcc will call it even
if you do not make it static, but then the behavior is undefined
-- it works only by luck. Whether this is "good luck" or "bad
luck" is not immediately obvious.
)