https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86125
--- Comment #2 from Martin Sebor <msebor at gcc dot gnu.org> --- It looks like this is on purpose. match_builtin_function_types, the function that determines whether a declaration of a built-in matches its expected type, has the following comment: /* Subroutine of compare_decls. Allow harmless mismatches in return and argument types provided that the type modes match. This function returns a unified type given a suitable match, and 0 otherwise. */ and it uses TYPE_MODE (type) to decide whether return and argument types are compatible. That makes size_t and char* match on targets where they are the same size, and ditto for int and char*. C++, on the other hand, handles this more sanely and diagnoses the mismatch in both cases: c.c:2:7: warning: declaration of ‘char* strdup(int)’ conflicts with built-in declaration ‘char* strdup(const char*)’ [-Wbuiltin-declaration-mismatch] char* strdup (int); // warning ^~~~~~ c.c:3:7: warning: declaration of ‘char* strlen(const char*)’ conflicts with built-in declaration ‘long unsigned int strlen(const char*)’ [-Wbuiltin-declaration-mismatch] char* strlen (const char*); // warning ^~~~~~