C-scanf()vs get()vs fgets())
我一直在做一个相当简单的程序,将字符串(假设输入了数字)转换为整数 完成后,我注意到一些我无法回答的非常奇怪的“bug”,主要是因为我对C-scanf()vs get()vs fgets()),c,scanf,fgets,gets,C,Scanf,Fgets,Gets,我一直在做一个相当简单的程序,将字符串(假设输入了数字)转换为整数 完成后,我注意到一些我无法回答的非常奇怪的“bug”,主要是因为我对scanf()、get()和fgets()函数的工作原理了解有限。(不过我确实读了很多文学作品。) 因此,在不编写太多文本的情况下,以下是程序代码: #include <stdio.h> #define MAX 100 int CharToInt(const char *); int main() { char str[MAX];
scanf()
、get()
和fgets()
函数的工作原理了解有限。(不过我确实读了很多文学作品。)
因此,在不编写太多文本的情况下,以下是程序代码:
#include <stdio.h>
#define MAX 100
int CharToInt(const char *);
int main()
{
char str[MAX];
printf(" Enter some numbers (no spaces): ");
gets(str);
// fgets(str, sizeof(str), stdin);
// scanf("%s", str);
printf(" Entered number is: %d\n", CharToInt(str));
return 0;
}
int CharToInt(const char *s)
{
int i, result, temp;
result = 0;
i = 0;
while(*(s+i) != '\0')
{
temp = *(s+i) & 15;
result = (temp + result) * 10;
i++;
}
return result / 10;
}
#包括
#定义最大值100
int CharToInt(常量字符*);
int main()
{
字符str[MAX];
printf(“输入一些数字(无空格):”;
获取(str);
//fgets(str,sizeof(str),stdin);
//scanf(“%s”,str);
printf(“输入的编号为:%d\n”,ChartPoint(str));
返回0;
}
int ChartPoint(常量字符*s)
{
int i,结果,温度;
结果=0;
i=0;
而(*(s+i)!='\0')
{
温度=*(s+i)和15;
结果=(温度+结果)*10;
i++;
}
返回结果/10;
}
这就是我一直遇到的问题。首先,当使用gets()
函数时,程序工作正常
其次,当使用fgets()
时,结果稍有错误,因为显然fgets()
函数最后读取换行符(ASCII值10),这会导致结果出错
第三,当使用scanf()
函数时,结果是完全错误的,因为第一个字符显然具有-52 ASCII值。对此,我没有任何解释
现在我知道不鼓励使用gets()
,所以我想知道是否可以在这里使用fgets()
,这样它就不会读取(或忽略)换行符。
另外,这个程序中的
scanf()
函数是怎么处理的?您不应该使用get
,这是正确的。如果要使用fgets
,只需覆盖换行符即可
char *result = fgets(str, sizeof(str), stdin);
char len = strlen(str);
if(result != NULL && str[len - 1] == '\n')
{
str[len - 1] = '\0';
}
else
{
// handle error
}
这并不假设没有嵌入的空值。另一个选项是POSIX:
getline
的优点是它为您进行分配和重新分配,处理可能的嵌入空值,并返回计数,这样您就不必在strlen
上浪费时间。请注意,不能将数组与getline
一起使用。指针必须为空或可自由移动
我不确定您在使用
scanf
时遇到了什么问题是的,您希望避免get
<如果缓冲区足够大,可以容纳新行,则code>fgets将始终读取新行(这可以让您知道缓冲区太小时,还有更多的行等待读取)。如果您想要像fgets
这样的东西不能读取新行(丢失缓冲区太小的指示),您可以将fscanf
与扫描集转换一起使用,例如:%N[^\N]“
,其中“N”被缓冲区大小-1替换
使用fgets
读取后,从缓冲区中删除尾随新行的一种简单(如果奇怪)方法是:strtok(buffer,“\n”)
这不是strtok的预期使用方式,但我经常以这种方式使用它,而不是以预期的方式(我通常避免使用这种方式)。尝试将fgets()与此修改后的ChartPoint()一起使用:
它本质上验证输入数字,并忽略任何其他内容。这是非常粗糙的,所以请根据口味修改它和盐。永远不要使用gets(),它可能会导致无法忍受的溢出。如果您的字符串数组大小为1000,并且我输入了1001个字符,则我可以使您的程序缓冲区溢出。- 切勿使用
。它不提供针对缓冲区溢出漏洞的保护(即,您无法告诉它您传递给它的缓冲区有多大,因此它无法阻止用户输入比缓冲区大的行并破坏内存)获取
- 避免使用
。如果不小心使用,它可能会出现与scanf
相同的缓冲区溢出问题。即使忽略这一点gets
- 通常,您应该改用
,尽管有时会不方便(你必须去掉换行符,你必须提前确定缓冲区的大小,然后你必须弄清楚如何处理过长的行-你是否保留你读的部分,放弃整个内容,动态增加缓冲区,然后再试一次,等等。)。有一些非标准函数可为您执行此动态分配(例如POSIX系统上的fgets
,函数)。请注意,getline
具有类似ggets
的语义,因为它为您去除了一个尾随换行符gets
scanf();
的问题。我认为scanf非常好,几乎可以用于所有事情,没有任何问题。但是你的结构并不完全正确。它应该是:
char str[MAX];
printf("Enter some text: ");
scanf("%s", &str);
fflush(stdin);
变量前面的“&”很重要。它告诉程序在哪里(哪个变量)保存扫描的值。
fflush(stdin);
从标准输入(键盘)中清除缓冲区,因此不太可能出现缓冲区溢出
gets/scanf和fgets之间的区别在于gets();
和scanf();
只扫描到第一个空格'
,而fgets();
扫描整个输入。(但之后一定要清理缓冲区,以免以后出现溢出)此代码存在许多问题。我们将修复名称不正确的变量和函数,并调查这些问题:
- 首先,
应重命名为适当的ChartPoint()
,因为它操作的是字符串,而不是单个字符StringToInt()
- 函数
[sic.]不安全。它不会检查用户是否意外传入空指针chartPoint()
- 它不会验证输入,或者更准确地说,跳过无效输入
int CharToInt(const char *s) { int i, result, temp; result = 0; i = 0; while(*(s+i) != '\0') { if (isdigit(*(s+i))) { temp = *(s+i) & 15; result = (temp + result) * 10; } i++; } return result / 10; }
char str[MAX]; printf("Enter some text: "); scanf("%s", &str); fflush(stdin);
n = x*10 = x*8 + x*2
#include <stdio.h> #include <ctype.h> // isdigit() // 1 fgets // 2 gets // 3 scanf #define INPUT 1 #define SIGNED 1 // re-implementation of atoi() // Test Case: 2147483647 -- valid 32-bit // Test Case: 2147483648 -- overflow 32-bit int StringToInt( const char * s ) { int result = 0, prev, msb = (sizeof(int)*8)-1, overflow; if( !s ) return result; while( *s ) { if( isdigit( *s ) ) // Alt.: if ((*s >= '0') && (*s <= '9')) { prev = result; overflow = result >> (msb-2); // test if top 3 MSBs will overflow on x*8 result *= 10; result += *s++ & 0xF;// OPTIMIZATION: *s - '0' if( (result < prev) || overflow ) // check if would overflow return prev; } else break; // you decide SKIP or BREAK on invalid digits } return result; } // Test case: 4294967295 -- valid 32-bit // Test case: 4294967296 -- overflow 32-bit unsigned int StringToUnsignedInt( const char * s ) { unsigned int result = 0, prev; if( !s ) return result; while( *s ) { if( isdigit( *s ) ) // Alt.: if (*s >= '0' && *s <= '9') { prev = result; result *= 10; result += *s++ & 0xF; // OPTIMIZATION: += (*s - '0') if( result < prev ) // check if would overflow return prev; } else break; // you decide SKIP or BREAK on invalid digits } return result; } int main() { int detect_buffer_overrun = 0; #define BUFFER_SIZE 2 // set to small size to easily test overflow char str[ BUFFER_SIZE+1 ]; // C idiom is to reserve space for the NULL terminator printf(" Enter some numbers (no spaces): "); #if INPUT == 1 fgets(str, sizeof(str), stdin); #elif INPUT == 2 gets(str); // can overflows #elif INPUT == 3 scanf("%s", str); // can also overflow #endif #if SIGNED printf(" Entered number is: %d\n", StringToInt(str)); #else printf(" Entered number is: %u\n", StringToUnsignedInt(str) ); #endif if( detect_buffer_overrun ) printf( "Input buffer overflow!\n" ); return 0; }