fgets
函数的声明如下所示:
char *fgets(char *str, int n, FILE *stream);
这意味着第二个参数应该是int
。
在以下程序中避免这种投射的正确方法是什么?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
const char *buffer = "Michi";
size_t len = strlen(buffer) + 1;
char arr[len];
printf("Type your Input:> ");
if (fgets(arr, (int)len, stdin) == NULL) {
printf("Error, fgets\n");
exit(1);
} else {
printf("Arr = %s\n", arr);
}
}
在这里,我使用(int)len
看起来很好,但是如果buffer
存储了一个非常长的字符串会发生什么?
让我们说:
const char *buffer = "Very long long ..."; /* where length is beyond the range of the `int` type */
我已经声明了size_t
类型的长度,这里很好,但是如果我将它传递给fgets
则不行,因为:
conversion to ‘int’ from ‘size_t {aka long unsigned int}’ may alter its value
如果我将其转换为int
,那么因为int
的大小小于length
的大小而导致某些信息丢失。} p>
也许我在这里错过了一些东西......
我应该怎样避免这种情况?
答案 0 :(得分:5)
#include <stdio.h>
char *fgets(char * restrict s, int n, FILE * restrict stream);
使用fgets()
时,无法使用输入缓冲区s[INT_MAX]
及更高版本。
OP的size_t len
代码可以通过转换为字符串并转换回int
来避免len
int
强制转换,这只是浪费。演员表演是正确的。
不是使用(int)
乱丢代码,而是减少/控制其使用并将强制转换包装在限制辅助函数中。
int fgets_len(size_t len) {
return (len < INT_MAX) ? (int) len : INT_MAX;
}
size_t len = something_big;
char *arr = malloc(len);
...
if ( fgets(arr, fgets_len(len), stdin) == NULL){
printf("Error, Fgets\n");
exit(1);
}else{
printf("Arr = '%s'\n", arr);
}
如果代码确实需要读取 long 行,请将ssize_t getline(char **lineptr, size_t *n, FILE *stream);
视为已定义here。请注意,这是一个非标准的C库函数,但其源代码随时可用。
关于fgets()
的迂腐使用,fgets()
返回NULL
至少有两个原因:文件结束和输入错误。现在考虑使用OP代码的角落案例
size_t len = strlen("") + 1;
char arr[len];
if ( fgets(arr, (int)len, stdin) == NULL){
和病理案例如
if ( fgets(arr, 0, stdin) == NULL){
if ( fgets(arr, -1, stdin) == NULL){
两者都在Is fgets() returning NULL with a short bufffer compliant?
进行了讨论答案 1 :(得分:2)
如果要避免整数溢出的麻烦,可以进行值检查并相应地执行操作。请注意,由于strlen()
返回类型size_t
的值,因此您有2个选项。声明int
类型的变量并为其指定strlen()
的返回值,该值将从size_t
类型到int
进行隐式转换,或者像在你的函数调用。
这是一个可能的解决方案:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int main(void)
{
const char *buffer = "Michi";
//size_t len = strlen(buffer) + 1;
size_t len = 9999999999;
if (len > INT_MAX) {
fprintf(stderr, "Error, length exceeded desired value.Aborting...\n");
return 1;
}
char arr[len];
printf("Type your Input:> ");
if (fgets(arr, (int)len, stdin) == NULL) {
printf("Error, Fgets\n");
exit(1);
}
else {
printf("Arr = %s\n", arr);
}
return 0;
}
答案 2 :(得分:1)
我根本不打算将参数的维度与fgets()
的维度相关联。
相反,我确保用于读取的缓冲区具有可以使用buffer
表示的长度(例如,不超过INT_MAX)。如果您想要真正可移植,请确保缓冲区长度不超过int
(标准指定32767
的最小允许值为INT_MAX
。)
然后利用32767
将读取部分行的事实,如果行长度超过缓冲区长度。
例如,假设fgets()
超过了从len
读取的任何行的长度;
stdin
请注意,如果文件末尾没有紧跟char arr[len] = {0};
char read_buffer[10]; /* I'm reasonably confident that 10 < 32767 */
while (fgets(read_buffer, 10, stdin) != NULL)
{
size_t read_length = strlen(read_buffer);
if (read_length > 0)
{
if (read_buffer[read_length-1] != `\n`)
{
strcat(arr, read_buffer);
}
else
{
strncat(arr, read_buffer, read_length-1);
printf("Arr = %s\n", arr);
arr[0] = '\0'; /* clear arr so next line may be read */
/* break here if want to stop reading after the first line */
}
}
,则上述内容将丢弃最后'\n'
之后的文字。
在上文中,将'\n'
替换为fgets(read_buffer, 10, stdin)
是安全的,因为价值小于或等于fgets(read_buffer, sizeof read_buffer, stdin)
的{{1}}始终可以安全地转换为{{1} }}。因此,如果您想要关闭编译器发出警告,您可以安全地转换ie size_t
答案 3 :(得分:0)
将整数转换为较短的有符号整数的结果,或 将无符号整数转换为有符号整数的结果 等长,如果值不能表示
是实现定义的。因此,如果您希望将unsigned类型的值安全地转换为signed,则必须确保所有可能的源值都可以使用target,signed type来表示。