Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why `gets_s()` still isn't implemented in GCC (9.3.0)?

Tags:

c

gcc

gets

c11

I know fgets() is a more common and widespread option for string input, but C11 has been around for 9 years. Why is gets_s() still out of work?

Even when I add -std=c11, it still doesn't work, even though gets_s() should be in stdio.h.

like image 492
brushmonk Avatar asked Nov 02 '25 06:11

brushmonk


2 Answers

Because it's optional. And the persons behind gcc seems to think it is a bad idea to include it. I don't know how they reasoned, but hints can be found in the C standard:

Recommended practice

The fgets function allows properly-written programs to safely process input lines too long to store in the result array. In general this requires that callers of fgets pay attention to the presence or absence of a new-line character in the result array. Consider using fgets (along with any needed processing based on new-line characters) instead of gets_s.

https://port70.net/~nsz/c/c11/n1570.html#K.3.5.4.1

If you want to use gets_s, then use another compiler. Or write your own wrapper, but don't call it gets_s because it's quite tricky to get it completely identical to the specs.

The C standard says this:

Runtime-constraints

s shall not be a null pointer. n shall neither be equal to zero nor be greater than RSIZE_MAX. A new-line character, end-of-file, or read error shall occur within reading n-1 characters from stdin.

If there is a runtime-constraint violation, s[0] is set to the null character, and characters are read and discarded from stdin until a new-line character is read, or end-of-file or a read error occurs.

Description

The gets_s function reads at most one less than the number of characters specified by n from the stream pointed to by stdin, into the array pointed to by s. No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array.

If end-of-file is encountered and no characters have been read into the array, or if a read error occurs during the operation, then s[0] is set to the null character, and the other elements of s take unspecified values.

There is one thing here that does not make sense at all. A runtime constraint is that s should not be a null pointer. On runtime constraint violoations, s[0] should be set to zero. But the operation s[0] = '\0' has undefined behavior if s is a null pointer.

Here is my take on trying to implement it, but IMO the specs are a mess, and I would not trust this. It was tricky to get it right.

char *my_gets_s(char *s, size_t n)
{
    if(!s) return NULL;

    size_t i=0;
    int ch;

    for(i=0; i<n-1; i++) {
        ch = fgetc(stdin);

        // If end-of-file is encountered and no characters have been read into the array,                          
        // or if a read error occurs during the operation, then s[0] is set to the null character                  
        if(ferror(stdin) || (ch == EOF && i == 0)) {
            s[0] = '\0';
            return NULL;
        }

        // If EOF and we have read at least one character                                                          
        if(ch == EOF) {
            s[0] = '\0';
            return s;
        }

        s[i] = ch;

        if(ch == '\n') {
            s[i] = '\0';
            return s;
        }
    }

    while ((ch = getchar()) != '\n' && ch != EOF);
    s[0] = '\0';
    return NULL;
}
like image 73
klutt Avatar answered Nov 04 '25 21:11

klutt


While it would be useful to have an alternative to fgets() which will always read an entire line, discarding excess information if need be, and report how many characters were read, gets_s is not such a function. The gets_s function would only be appropriate in scenarios in which any over-length input lines should be completely discarded. The only good ways of performing line-based I/O are either to build one's own line-input routine based upon fgetc() or getchar(), use fgets() with corner-case logic that's as big as a character-based get-line routine, or--if one wants to maximize performance and the stream doesn't have to be shared with anything else--use fread() and memchr(), persisting read data in a private buffer between calls to the get-line routine.

like image 26
supercat Avatar answered Nov 04 '25 21:11

supercat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!