cstrchr

find = strchr(st, '\n'); replaced with *find = '\0';


I read a snippet of code from C Primer Plus, and tried hard to understand *find = '\0';

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

char *s_gets(char *st, int n);

struct book {
    char title[40];
    char author[40];
    float value;
}

int main(void) {
    ...
}

char *s_gets(char *st, int n) {
    char *ret_val;
    char *find;

    ret_val = fgets(st, n, stdin);
    if (ret_val) {
        find = strchr(st, '\n'); //look for newline
        if (find)                // if address is not null
            *find = '\0';        //place a null character there
        else
            while (getchar() != '\n')
                continue;  //dispose rest of line
    }
    return ret_val;
}

For what purpose should find = strchr(st, '\n'); be followed by *find = '\0';

I searched strchr but found it an odd name although could get idea about it's function. Does the name strchr come from stringcharacter?


Solution

  • The code using find = strchr(s, '\n') and what follows zaps the newline that was read by fgets() and included in the result string, if there is indeed a newline in the string. Often, you can use an alternative, more compact, notation:

    s[strcspn(s, "\n")] = '\0';
    

    which is written without any conditional code visible. (If there's no newline, the null byte overwrites the existing null byte.)

    The overall objective seems to be to make s_gets() behave more like an antique, dangerous and no longer standard function, gets(), which reads up to and including a newline, but does not include the newline in the result. The gets() function has other design flaws which make it a function to be forgotten — never use it!

    The code shown also detects when no newline was read and then goes into a dangerous loop to read the rest of the line. The loop should be:

    else
    {
        int c;
        while ((c = getchar()) != EOF && c != '\n')
            ;
    }
    

    It is important to detect EOF; not all files end with a newline. It is also important to detect EOF reliably, which means this code has to use int c (whereas the original flawed loop could avoid using a variable like c). If this code carelessly used char c instead of int c, it could either fail to detect EOF altogether (if plain char is an unsigned type) or it could give a false positive for EOF when the data being read contains a byte with value 0xFF (if plain char is a signed type).

    Note that using strcspn() directly as shown is not an option in this code because then you can't detect whether there was a newline in the data; you merely know there is no newline in the data after the call. As Antti Haapala points out, you could capture the result of strcspn() and then decide whether a newline was found and therefore whether to read to the end of line (or end of file if there is no EOL before the EOF).