The strchr
function in the C standard library looks for a char
in a string, but its signature takes an int
for the search character. In these two implementations I found, the implementation casts this int
to a char
:
char *strchr(const char *s, int c) { while (*s != (char)c) if (!*s++) return 0; return (char *)s; } char *strchr(const char *s, int c) { while (*s && *s != (char)c) s++; if (*s == c) return (char *)s; return NULL; }
Does anyone know why? Why not just take a char
as a parameter?
The strchr() function returns a pointer to the first occurrence of c that is converted to a character in string. The function returns NULL if the specified character is not found.
Description. The C library function char *strchr(const char *str, int c) searches for the first occurrence of the character c (an unsigned char) in the string pointed to by the argument str.
Explanation: The function char *strrchr(ch, c) returns pointer to last occurrence of c in ch or NULL if not present. 4.
In C++, strchr() is a predefined function. It is used for string handling and it returns the first occurance of a given character in the string provided. The syntax of strchr() is given as follows. char *strchr( const char *str, int c) In the above syntax, str is the string that contains the character c.
The reasons for that are purely historical. Note, that in the old days of C language (K&R C) there was no such thing as function prototype. A strchr
function in those times would be declared as
char *strchr();
and defined in K&R style as
char *strchr(s, c) char *s; char c; { /* whatever */ }
However, in C language (in K&R C and in the modern one as well) if the function is declared without a prototype (as shown above), the parameters passed in each function call are subjected to so called default argument promotions. Under default argument promotions any integral type smaller than int
(or unsigned int
) is always converted to int
(or unsigned int
). I.e. when the parameters are undeclared, whenever you pass a char
value as an argument, this value is implicitly converted to int
, and actually physically passed as an int
. The same is true for short
. (BTW, float
is converted to double
by default argument promotions). If inside the function the parameter is actually declared as a char
(as in the K&R style definition above), it is implicitly converted back to char
type and used as a char
inside the function. This is how it worked in K&R times, and this actually is how it works to this day in modern C when function has no prototype or when variadic parameters are used.
Now, cue in the modern C, which has function prototypes and uses modern-style function definition syntax. In order to preserve and reproduce the "traditional" functionality of strchr
, as described above, we have no other choice but to declare the parameter of strchr
as an int
and explicitly convert it to char
inside the function. This is exactly what you observe in the code you quoted. This is exactly as the functionality of strchr
is described in the standard.
Moreover, if you have an already-compiled legacy library, where strchr
is defined in K&R style as shown above, and you decided to provide modern prototypes for that library, the proper declaration for strchr
would be
char *strchr(const char *s, int c);
because int
is what the above legacy implementation expects to physically receive as c
. Declaring it with a char
parameter would be incorrect.
For this reason, you will never see "traditional" standard library functions expecting parameters of type char
, short
or float
. All these functions will be declared with parameters of type int
or double
instead.
A very same rationale is behind the standard guarantee that char pointers and void *
pointers share the same representation and alignment requirements. Relying on this guarantee you can declare malloc
as a void *
-returning function and then use this declaration with a pre-compiled legacy version of standard library where malloc
actually returned char *
.
Reference: the C99 rationale, version 5.10
7.1.4 Use of library functions
/--/
All library prototypes are specified in terms of the “widened” types: an argument formerly declared as char is now written as int. This ensures that most library functions can be called with or without a prototype in scope, thus maintaining backwards compatibility with pre-C89 code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With