简体   繁体   中英

Why does strchr take an int for the char to be found?

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 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 ). Ie 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

In c the type of a character literal is int . For example: 'a' is of type int .

I think this can be attributed to nothing more than an accident of history. You're exactly right that char seems the obvious data type to use for the character being searched for.

In some situations in the C library, such as the getc() function, an int value is returned for the character read from input. This is not a char because an extra non-character value ( EOF , usually -1) can be returned to indicate the end of the character stream.

The EOF case doesn't apply to the strchr() function, but they can't really go back and change the declaration of the function in the C library now.

int c is the character that you want to search. The character is passed as an integer, but in fact only the lower 8 bits are searched. It should therefore be handed over to a char

The strchr function looks like this:

char *strchr(const char *s, int c){
    while (*s != (char)c)
        if (!*s++)
            return 0;
    return (char *)s;
}

As you can see there is a cast of int c to (char)c .

Now to Answer to your question, your char ch it is converted to an integer int c and applied as the ordinal value of a character.

So the following program should be OK:

#include<stdio.h>
#include<string.h>

int main(void){
    char *name = "Michi";
    int c = 99; /* 99 is the ANSI code of c*/

    char *ret = strchr(name, c);

    printf("String after %s\n", ret);
    return 0;
}

But the following not:

#include<stdio.h>
#include<string.h>

int main(void){
    char *name = "Michi";
    char c = '99'; /* 99 is the ANSI code of c*/

    char *ret = strchr(name, c);

    printf("String after %s\n", ret);
    return 0;
}

Because of multi-character character constant which is overflow in implicit constant conversion

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM