为什么 `gets_s()` 仍然没有在 GCC (9.3.0) 中实现?
Why `gets_s()` still isn't implemented in GCC (9.3.0)?
我知道 fgets()
是更常见和更广泛的字符串输入选项,但 C11 已经存在 9 年了。为什么 gets_s()
仍然没有工作?
即使我添加 -std=c11
,它仍然不起作用,即使 gets_s()
应该在 stdio.h
.
因为它是可选的。 gcc
背后的人似乎认为将它包括在内是个坏主意。我不知道他们是怎么推理的,但是在C标准中可以找到提示:
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
如果要使用gets_s
,则使用其他编译器。或者编写您自己的包装器,但不要将其命名为 gets_s
,因为要使其与规格完全相同非常棘手。
C 标准是这样说的:
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.
这里有一件事完全没有意义。运行时约束是 s
不应该是空指针。在运行时约束违规时,s[0]
应设置为零。但是如果 s
是一个空指针,操作 s[0] = '[=24=]'
有未定义的行为。
这是我尝试实现它的想法,但 IMO 的规范一团糟,我不相信这一点。很难做到正确。
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] = '[=10=]';
return NULL;
}
// If EOF and we have read at least one character
if(ch == EOF) {
s[0] = '[=10=]';
return s;
}
s[i] = ch;
if(ch == '\n') {
s[i] = '[=10=]';
return s;
}
}
while ((ch = getchar()) != '\n' && ch != EOF);
s[0] = '[=10=]';
return NULL;
}
正如其他人指出的那样,gets_s()
是:
- 可选(许多编译器实际上不实现它)
- 从C11开始(所以以前的标准肯定没有)
如果你真的需要一些东西而不是fgets()
,那么你可以自己实现包装器,例如:
char* myGets(char* str, int count)
{
if (fgets(str, count, stdin)) {
for (int i = 0; i < count; ++i) {
if (str[i] == '\n') {
str[i] = '[=10=]';
break;
}
}
return str;
} else {
return NULL;
}
}
虽然有一个 fgets()
的替代方法会很有用,它总是读取整行,在需要时丢弃多余的信息,并报告读取了多少字符,但 gets_s
不是这样的功能。 gets_s
函数仅适用于应完全丢弃任何超长输入行的场景。执行基于行的 I/O 的唯一好方法是根据 fgetc()
或 getchar()
构建自己的行输入例程,将 fgets()
与极端情况逻辑一起使用与基于字符的 get-line 例程一样大,或者——如果想要最大化性能并且流不必与其他任何东西共享——使用 fread()
和 memchr()
,坚持在调用 get-line 例程之间读取专用缓冲区中的数据。
我知道 fgets()
是更常见和更广泛的字符串输入选项,但 C11 已经存在 9 年了。为什么 gets_s()
仍然没有工作?
即使我添加 -std=c11
,它仍然不起作用,即使 gets_s()
应该在 stdio.h
.
因为它是可选的。 gcc
背后的人似乎认为将它包括在内是个坏主意。我不知道他们是怎么推理的,但是在C标准中可以找到提示:
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
如果要使用gets_s
,则使用其他编译器。或者编写您自己的包装器,但不要将其命名为 gets_s
,因为要使其与规格完全相同非常棘手。
C 标准是这样说的:
Runtime-constraints
s
shall not be a null pointer.n
shall neither be equal to zero nor be greater thanRSIZE_MAX
. A new-line character, end-of-file, or read error shall occur within readingn-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 byn
from the stream pointed to by stdin, into the array pointed to bys
. 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.
这里有一件事完全没有意义。运行时约束是 s
不应该是空指针。在运行时约束违规时,s[0]
应设置为零。但是如果 s
是一个空指针,操作 s[0] = '[=24=]'
有未定义的行为。
这是我尝试实现它的想法,但 IMO 的规范一团糟,我不相信这一点。很难做到正确。
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] = '[=10=]';
return NULL;
}
// If EOF and we have read at least one character
if(ch == EOF) {
s[0] = '[=10=]';
return s;
}
s[i] = ch;
if(ch == '\n') {
s[i] = '[=10=]';
return s;
}
}
while ((ch = getchar()) != '\n' && ch != EOF);
s[0] = '[=10=]';
return NULL;
}
正如其他人指出的那样,gets_s()
是:
- 可选(许多编译器实际上不实现它)
- 从C11开始(所以以前的标准肯定没有)
如果你真的需要一些东西而不是fgets()
,那么你可以自己实现包装器,例如:
char* myGets(char* str, int count)
{
if (fgets(str, count, stdin)) {
for (int i = 0; i < count; ++i) {
if (str[i] == '\n') {
str[i] = '[=10=]';
break;
}
}
return str;
} else {
return NULL;
}
}
虽然有一个 fgets()
的替代方法会很有用,它总是读取整行,在需要时丢弃多余的信息,并报告读取了多少字符,但 gets_s
不是这样的功能。 gets_s
函数仅适用于应完全丢弃任何超长输入行的场景。执行基于行的 I/O 的唯一好方法是根据 fgetc()
或 getchar()
构建自己的行输入例程,将 fgets()
与极端情况逻辑一起使用与基于字符的 get-line 例程一样大,或者——如果想要最大化性能并且流不必与其他任何东西共享——使用 fread()
和 memchr()
,坚持在调用 get-line 例程之间读取专用缓冲区中的数据。