1

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?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
AbstProcDo
  • 19,953
  • 19
  • 81
  • 138
  • 1
    http://man7.org/linux/man-pages/man3/strchr.3.html , https://www.tutorialspoint.com/c_standard_library/c_function_strchr.htm .. ? Search harder: "[strchr] searches for the first occurrence of the character [..] in the string [..]" – user2864740 Oct 18 '18 at 03:00
  • 1
    It zaps the newline that was read by `fgets()` and included in the result string, if there is indeed a newline in the string. An alternative, more compact, notation is `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()`](https://stackoverflow.com/questions/1694036/), which removes the newline. – Jonathan Leffler Oct 18 '18 at 03:00

1 Answers1

5

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278