转自 http://blog.sina.com.cn/s/blog_67d069a90100ul7h.html
头文件<stdio.h>
例子:
int a;
scanf(“%d”,&a); //输入取地址,即变量地址
printf(“%d\n”,a); //输出指定对象,即变量名
scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数,scanf函数仅在每一个数据域均有数据,并按回车后结束)。
① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。
----------个人理解:scanf()函数工作的机理就是:在输入流中取所需要的数据,在输入流stdin中个数据是按照空格(含一个或多个)隔开的,如果输入流中已经取完了数据,则要求再次往输入流中输入数据。
注意的是:只有字符char在输入流中的获取会承认空格或回车中的换行符为所要取的值,别的如字符串或int类型均不认为空格或回车中的换行符为其值。
看下面:
#include "stdafx.h"
#include <stdio.h>
#include<windows.h>
int main()
{
char str[80];
char str1[80];
char str2[80];
scanf("%s",&str); //不妨输入I love you!
printf("%s\n",str);
Sleep(3000);
printf("over!\n");
scanf("%s",str2);
printf("%s\n",str1);
printf("%s\n",str2);
return 0;
}
输入I love you!便回车,结果系统直接运行到结束,不会再提示你输入str1,str2,因为scanf()函数实质就是从标准输入stdin中取数据,而是不从键盘取数据,只有标准输入区中无数据才会提示从键盘输入数据(其实质为向标准输入区中输入数据)。
对于scanf("%s",&str);这句由于刚开始标准输入缓冲区中无数据,故需要提示从键盘中输入数据,但你输入的为”I love you!”,实际上在标准输入缓冲中按照输入的”I loveyou!”进行暂时存储,标准输入缓冲区中数据以空格为间隔,故此时str实际对应的为”I”。
对于scanf("%s",str1); scanf("%s",str2);这两句,由于scanf()从标准输入缓冲区中取值,现在标准输入缓冲区中还剩余” love you!”,注意love前有空格,但字符数组或字符串不承认空格,故str1对应love,str2对应you!。
如下面程序:
#include "stdafx.h"
#include <stdio.h>
#include<windows.h>
int main()
{
char str[80];
char str1[80];
char str2[80];
int a;
int b;
printf("%s\n",str);
Sleep(3000);
printf("over!\n");
//scanf("%s",str2);
printf("%d\n",a);
scanf("%d",&b);
printf("%d\n",b);
return 0;
}
若输入”fhdsia 12 34”,说明一下12前以及34前有很多空格,这样程序会直接运行结束,中途不会再提示输入数据。
开始标准输入缓冲区中没有数据,故scanf("%s",&str);系统会提示输入数据,若输入为”fhdsia 12 34”,则str对应”fhdsia”。
对应scanf("%d",&a);
printf("%d\n",a);
由于现在标准输入中已经有数据,故不会再提示输入数据,int类型不承认空格,故a对应12,b对应34。
若输入”fhdsia 12 “,这样系统还会提示你再输入一次数据,对应语句为scanf("%d",&b);
#include "stdafx.h"
#include <stdio.h>
#include<windows.h>
int main()
{
char str[80];
char str1[80];
char str2[80];
char a;
char b;
scanf("%s",&str);
printf("%s\n",str);
Sleep(3000);
printf("over!\n");
//scanf("%s",str2);
scanf("%c",&a);
printf("%c\n",a);
scanf("%c",&b);
printf("%c\n",b);
return 0;
}
若输入”afdsf ”然后回车,注意afsdf后面空格大于2个,则由于char会把标准输入区的空格或回车当做输入值,故输入”afdsf ”(2个或2个以上的空格)后,程序直接运行结束,
scanf("%c",&a);
printf("%c\n",a);
scanf("%c",&b);
a对应标准输入区中的空格,同理b也对应空格。
若输入”afdsf”后直接回车,这样,str对应afdsf,a对应回车(个人认为其为空格),这样还会提示继续输入数据用来对应b。
函数名: fflush
功 能: 清除一个流
C和C++的标准里从来没有定义过 fflush(stdin)。可用getchar();(但并不等价,因为getchar()只相当于从标准输入缓冲区中去读取按顺序对应的一个字符(包含空格或回车),就这样的功能。)代替。
某些编译器(如VC6)支持用 fflush(stdin)来清空输入缓冲,但是并非所有 编译器都要支持这个功能(linux 下的gcc 就不支持),因为标准中根本没有定 义 fflush(stdin)。
scanf("%c",&c);我们每击打一下"Enter"键,向键盘缓冲区发去一个“回车”(\r),一个“换行"(\n),在这里\r被scanf()函数处理掉了(姑且这么认为吧^_^),而\n被scanf()函数“错误”地赋给了c.
#include "stdafx.h"
#include <stdio.h>
int main()
{
char string[50];
scanf("%[^\n]",string);
printf("%s\n",string);
return 0;
}
输入”adasf fsd as”, 则输出就是输入。
------ %[] 扫描字符集合,直到回车换行符号为结束。
使用scanf函数进行输入,必须指定输入的数据的类型和格式,不仅繁琐复杂,而且很容易出错.C++保留scanf只是为了和C兼容,以便过去用C语言写的程序可以在C++的环境下运行。C++的编程人员都愿意使用cin进行输入,很少使用scanf。
在windows平台下从控制台无回显地取一个字符,在linux下是有回显的。
-------只能输入一个字符。
#include "stdafx.h"
#include <stdio.h>
#include <conio.h> //window 平台
#include <stdlib.h>
int main(void)
{
char ch;
printf("Input a character:");
ch = getch();
printf("\nYou input a %c\n", ch);
int a;
a = getch();
printf("\nYou input a %d\n", a);
system("pause");
return 0;
}
---输入’d’后直接运行得到Youinput a d。然后让你继续输入a对应的值,但你只能输入一个字符,比如输入’1’,则输出的结果显示为49,即1的ASCII码。
getch()是非缓冲输入函数,就是不能用getch()来接受缓冲区已存在的字符。就是指用到getch(),每次必须再键盘上输入,不会读取stdin中的数值。
这个函数可以让用户按下任意键而不需要回车就可以接受到用户的输入。可以用来作为“press any key to continue”的实现。
头文件<stdio.h>
功能
从stdin流中读字符。
getchar()只相当于从标准输入缓冲区中去读取按顺序对应的一个字符(包含空格或回车),就这样的功能。
int a;a=getchar();printf("%d\n",a);输入12,则输出为49,即1对应的ASCII码。若为%c,则输出为1,这个1为字符1。
函数名: getc
功 能: 从流中取字符
int getc(FILE *stream);//read the next character from stream andreturn it as an unsigned char cast to a int ,or EOF on end of file or error.
在C语言中,用函数getc(fgetc)从文件读取字符。getc、fgetc用法相同。
----注意针对的是字符。
六、gets()
以回车换行符为结束标志。
函数:gets(字符指针)
头文件:string.h(c中),c++不需包含此头文件
原型:char *gets( char *buffer );
功能:从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。换行符不作为读取串的内容,读取的换行符被转换为null值,并由此来结束字符串。
返回值:读入成功,返回与参数buffer相同的指针;读入过程中遇到EOF(End-of-File)或发生错误,返回NULL指针。所以在遇到返回值为NULL的情况,要用ferror或feof函数检查是发生错误还是遇到EOF。
注意:本函数可以无限读取,不会判断上限,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。如果溢出,多出来的字符将被写入 到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值,为了避免这种情况,我们可以用gets_s()来替换gets()。这个事实导致 gets函数只适用于玩具程序。
gets()函数用来从标准输入设备(键盘)读取字符串直到换行符结束,但换行符会被丢弃,然后在末尾添加'\0'字符。其调用格式为:
gets(s);
其中s为字符串变量(字符串数组名或字符串指针)。
gets(s)函数与scanf("%s:",&s)相似,但不完全相同,使用scanf("%s",&s);函数输入字符串时存在 一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。
说明:
gets(s);函数中的变量s为一字符串。如果为单个字符,编译连接不会有错误,但运行后会出现”Null pointer asignment"的错误。
#include<iostream>
using namespace std;
int main()
{
char str[100];
gets(str);
cout<<str<<endl;
return 0;
}
把一个字符退回到输入流中
用 法
int ungetc(char c, FILE *stream);
c 要写入的字符,stream 文件流指针
字符c - 操作成功,EOF - 操作失败
#include "stdafx.h"
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch;
printf( "Enter an integer: " );
ch = getchar() ;
printf("%d\n",ch);
ungetc( ch, stdin );
int s;
s=getchar();
printf("%d\n",s);
return 0;
}
输入12,则会运行结束,不再要求输入数据,输出为49(换行)49
另一篇
(1) int getchar(void)
函数每次从stdin缓冲区读入一个字符遇到回车返回,并且回车也被存入缓冲区,所以getchar可以用来吃掉回车符。
函数返回读到的第一个字符的ASC码值,如果失败则返回-1
其定义为宏定义,即 #define getchar() getc(stdin)
因为是宏定义函数,所以getchar不支持指针的引用。
现在解释跳过第二个输入的原因,假如我们第一次输入a按回车,这时候我们getchar实际上将a字符和回车放入了缓冲区中,并返回第一个字符的值,然后第二个getchar函数直接从缓冲区继续去字符,拿到了回车,没有需要用户再输入。为什么会有这种机制,可以参考
http://www.cnblogs.com/octobershiner/archive/2011/12/06/2278492.html
所以很多人把getchar用来吃掉回车符,起到类似清空缓冲区的原因。清空缓冲区还可以使用fflush函数,但是这个函数不是C标准库中的函数,有时候是无效的。
刚才说到了getchar的定义,其实就是getc函数的一种特殊的情况,下面介绍getc函数。
(2) int getc(FILE* stream)
也是采用的宏定义,所以不支持函数指针调用。
#define getc(_stream) (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
getc从指定的流中读取一个字符,刚才的getchar实际上就是getc(stdin) ,stdin是标准输入流,C在stdio.h中定义了三个流,也可以理解为是缓冲区。
#define stdin (&_iob[0]) //标准输入,一般指向键盘
#define stdout (&_iob[1]) //标准输出
#define stderr (&_iob[2]) //错误流
基本实现过程
在这里再补充两个函数,简单带过,因为getch和getche不是C标准的库函数。
(3) int getch(void)
从命令行读取一个字符,不显示在命令行,很多人用来模拟“按任意键继续的效果”
(4) int getche(void)
从命令行中读取一个字符,显示在,命令行,与getch一样不是标准C函数库中的函数,在Windows平台下包含与conio.h中,在linux下与之相对的是curses.h库,但是在cygwin模拟的UNIX和ubuntu linux下gcc编译器均无法使用。
本文的最后介绍一下gets函数,他从流中读取字符串直到遇到换行符或者EOF或者遇到错误.
(5) char* gets(char* buffer)
从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。换行符不作为读取串的内容,读取的换行符被转换为null值,并由此来结束字符串。
其实gets这个函数还是有很多安全性的问题需要注意的,比如他要一直等EOF或者换行符才会结束,所以当程序无法预知可能存在的输入串长度时,是非常危险的,上一篇文章提到过缓冲区溢出的攻击,那么这里就是一个漏洞,我们不可能和攻击者比buffer的大小,及时我们定义很大很大的缓冲区,攻击者却能输入一个更长的字符串。
其中VS 2005中提供了一个新的函数gets_s函数,带有安全性的gets,但是显然这不是标准库中所定义的。
========f的分割线=================
stdio.h中还包含了与gets和getc想对的文件操作函数,fgets和fgetc
char *fgets(char *s, int n, FILE *stream); // n表示一次读入数据的长度,从stream中读取存入到s串中,遇到换行和结束符则结束
int fgetc(FILE * stream);
除了getch和getche,其他的都是C标准库中的头文件,包含在头文件stdio.h中。
int fgetc ( FILE * stream ); //从流stream中读一个字符。可以将标准输入stdin作为它的实参,这时候从标准输入读取一个字符。
int getc(FILE * stream); //和fgetc等效,由fgetc通过宏实现。
int getchar ( void ); //从标准输入stdin读入一个字符,程序等待你输入的时候,你可以输入多个字符,回车后程序继续执行。
//但getchar只读入一个字符
说明:getc、getchar都是通过宏定义借助fgetc实现。如getchar的实现为,#define getchar() fgetc(stdin)。
char * fgets (char * str, int num, FILE *stream);
//从流stream中读入最多num个字符到字符数组str中,当遇到换行符时、或读到num-1个字符时停止。
//自动加上'\0'空字符结尾
char * gets ( char * str ); //从标准输入stdin读取一个字符串,遇到换行或结束时候终止。
//不同于fgets,他没有指定num,所以需要注意字符数组str的大小。
说明: fgets和gets之间没有宏定义的关系,彼此各自有自己的实现。蠕虫病毒的实现就是函数gets的“功劳”。gets函数的任务是从流中读入一个字符串。 它的调用者会告诉它把读入的字符串放在什么地方。但是,gets()函数并不检查缓冲区大小,如果调用者提供了一个指向堆栈的指针,并且get()函数读入的字符数量超过了超过 了缓冲区的空间大小,get()会愉快地将多出来的字符继续写入到堆栈中,这就覆盖了堆栈中原来的内容。如:main() { char line[ 512 ]; // 在程序的堆栈上分配512个字符的空间 ... gets(line); // 蠕虫病毒的入口,可以将恶意代码通过多出来的数据写入堆栈 }
建议不要用getch和getche,因为它们不是C标准库中的函数。用它们写出的程序可移植性差,不同的编译器不保证可以包含conio.h。建议用fgets函数彻底替代gets函数。
另外,绝大多数的这些get函数,都有对应的put版本。
int fputc ( int character, FILE * stream );
int putc ( int character, FILE * stream ); //通过宏定义和fputc实现
int putchar ( int character ); //通过宏定义实现:#define putchar(c) fputc(c, stdout)
int fputs ( const char * str, FILE * stream );
int puts ( const char * str );
说明:两者之间无宏定义实现关系。puts(const char *str)近似等效于fputs(cosnt char *str, stdout),不同点是前者还输出一个'\n'
最后,关于EOF
EOF是在stdio.h文件中定义的符号常量,值为-1。如,
fputc函数返回一个值:如果输出成功则返回值就是输出的字符;如果输出失败,则返回一个EOF。
fgetc函数读字符时遇到文件结束符,函数返回一个文件结束标记EOF。如果想从一个磁盘文件顺序读入字符并在屏幕上显示,可以:
while (ch != EOF){
putchar(ch);
ch = fgetc(fp);
}
注意,EOF不是可输出字符,因此不能在屏幕上显示。由于ASCII码不可能出现-1,因此EOF定义为-1是合适的。当读入的字符值等于-1(即EOF)时,表示读入的已不是正常的字符,而是文件结束符。但以上只适用于读取文本文件的情况。现在ANSI C已经允许用缓冲文件系统处理二进制文件,而读入某一个字节中的二进制数据的值有可能是-1,而这又恰好是EOF的值。这就出现了需要读入有用数据,却处理为“文件结束”。feof(fp) 用来测试fp所指向的文件当前状态是否是“文件结束”。如果想顺序读入一个二进制文件数据,可以:
c = fgetc(fp);
...
}