标准输入函数

在stdio.h中scanf声明如下:

/* Read formatted input from stdin.This function is a possible cancellation point and therefore notmarked with __THROW. */
extern int scanf (const char *__restrict __format, ...) __wur;

使用Mac或Linux的同学,在终端上输入man scanf回车即可学习scanf函数的用法。我们可以看到注释上说明,scanf从标准输入stdin输入读取数据,在glibc中stdin的定义如下:

/*stdio.c*/
FILE *stdin = (FILE *) &_IO_2_1_stdin_;
/*libio.h*/
extern struct _IO_FILE_plus _IO_2_1_stdin_;
/*libioP.h*/
struct _IO_FILE_plus
{FILE file;const struct _IO_jump_t *vtable;
};

从以上代码我们可以知道,最终stdin是一个FILE文件流指针,我能继续追踪FILE类型为何物。

/** stdio state variables.** The following always hold:** if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR),* _lbfsize is -_bf._size, else _lbfsize is 0* if _flags&__SRD, _w is 0* if _flags&__SWR, _r is 0** This ensures that the getc and putc macros (or inline functions) never* try to write or read from a file that is in `read' or `write' mode.* (Moreover, they can, and do, automatically switch from read mode to* write mode, and back, on "r+" and "w+" files.)** _lbfsize is used only to make the inline line-buffered output stream* code as compact as possible.** _ub, _up, and _ur are used when ungetc() pushes back more characters* than fit in the current _bf, or when ungetc() pushes back a character* that does not match the previous one in _bf. When this happens,* _ub._base becomes non-nil (i.e., a stream has ungetc() data iff* _ub._base!=NULL) and _up and _ur save the current values of _p and _r.** NB: see WARNING above before changing the layout of this structure!*/
typedef  struct __sFILE {unsigned char *_p; /* current position in (some) buffer */int  _r; /* read space left for getc() */int  _w; /* write space left for putc() */short  _flags; /* flags, below; this FILE is free if 0 */short  _file; /* fileno, if Unix descriptor, else -1 */struct  __sbuf _bf;  /* the buffer (at least 1 byte, if !NULL) */int  _lbfsize; /* 0 or -_bf._size, for inline putc *//* operations */void  *_cookie; /* cookie passed to io functions */int  (* _Nullable _close)(void *);int  (* _Nullable _read) (void *, char *, int);fpos_t  (* _Nullable _seek) (void *, fpos_t, int);int  (* _Nullable _write)(void *, const char *, int);/* separate buffer for long sequences of ungetc() */struct  __sbuf _ub;  /* ungetc buffer */struct __sFILEX *_extra; /* additions to FILE to not break ABI */int  _ur; /* saved _r when _r is counting ungetc data *//* tricks to meet minimum requirements even when malloc() fails */unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */unsigned char _nbuf[1]; /* guarantee a getc() buffer *//* separate buffer for fgetln() when line crosses buffer boundary */struct  __sbuf _lb;  /* buffer for fgetln() *//* Unix stdio files get aligned to block boundaries on fseek() */int  _blksize; /* stat.st_blksize (may be != _bf._size) */fpos_t  _offset; /* current lseek offset (see WARNING) */
} FILE;

看到这个结构体内部一大堆成员变量不要慌,我们重点关注里面的close、read、seek和write函数指针。我们在调用scanf函数时正是通过这几个函数指针间接调用系统函数close、read、seek和write实现标准输入关闭、读取、偏移和写功能。

int  (* _Nullable _close)(void *);
int  (* _Nullable _read) (void *, char *, int);
fpos_t  (* _Nullable _seek) (void *, fpos_t, int);
int  (* _Nullable _write)(void *, const char *, int);

从函数声明我们知道scanf返回一个int型返回值,在调用时scanf,返回正整数表示从标准输入读取到的有效数据数量,返回0表示没有输入或者输入不正确,返回负数表示发生了从标准输入读取数据发生了错误。下面我们使用scanf从标准输入读取数据的代码。

int num = 0;
float f_num = 0;int count = scanf("%d", &num);
scanf("%f", &f_num);scanf_s("%d", &num);

在scanf中输入数据并将数据保存在变量num和f_num中,调用scanf输入数据必须要用%,%d表示输入一个整数,%f表示输入一个单精度浮点数,count保存scanf输入数据的有效数。

在C语言里函数传参方式有2种,一种是传值另外一种是传指针。通过传值方式形参拷贝实参,得到一个实参副本对实参副本进行修改不会影响实参,而传指针方式,将会得到实参的地址,通过指针解引用可以间接修改实参的值。那么回到scanf函数那里,我们通过对变量进行取址,scanf函数内部有一个指针,将变量地址值赋给内部指针,再将标准输入的值赋值给实参,实参变量因此获得标准输入的值。

标准输出函数

在stdio.h中printf函数声明如下:

/* Write formatted output to stdout.This function is a possible cancellation point and therefore notmarked with __THROW. */
extern int printf (const char *__restrict __format, ...);

看到这里是不是很熟悉?printf函数的返回值也是int型,调用printf函数将会返回输出字符个数,出错则返回一个负数。同样在Linux/Mac平台的终端上输入man printf函数可以查看函数的详细使用方法(任何C标准函数都可以在Linux/Mac平台上输入man+函数名的方式查看函数使用方法)。下面是我们使用printf函数在标准输出中输出数据的代码。

int output_count = printf("num = %d\n", num);
printf("output_count = %d\n", output_count);output_count = printf("f_num = %f\n", f_num);
printf("output_count = %d\n", output_count);

在代码片段里我们看到一个\n字符,在C语言里这是一个换行符。看到这里是不是又有疑问了,为什么printf函数输出变量值时不需要对变量取地址?这就回到前面我们说过的问题了,在C语言里传值,形参是实参的副本,形参修改了不会影响到实参。而printf函数只是在标准输出中输出信息,不会修改实参的值,因此使用传值方式。

那么标准输出是什么呢?从print函数声明代码注释上看,标准输出正是stdou,我们继续在glibc中继续追踪stdout到底是什么?在stdout.c中我们看到stdout和stderr定义如下:

FILE *stdout = (FILE *) &_IO_2_1_stdout_;
FILE *stderr = (FILE *) &_IO_2_1_stderr_;

我们发现stdout、stderr和stdin的定义一模一样都是一个FILE类型指针,那么使用方式就和stdin一样了,区别则在于stdin和文件描述符0绑定,stdout和文件描述符1绑定,stderr和文件描述符2绑定。

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

C语言入门基础之输入和输出相关推荐

  1. c语言中return的作用_C语言入门基础整理

    学习计算机技术,C语言可以说是必备的,他已经成为现在计算机行业人学习必备的,而且应用也是十分的广泛,今天就来看看拥有几年c语言工作经验的大神整理的C语言入门基础知识,没有学不会,只有不肯学. 结构化程 ...

  2. c语言3到7位水仙花数流程图_C语言入门基础整理

    学习计算机技术,C语言可以说是必备的,他已经成为现在计算机行业人学习必备的,而且应用也是十分的广泛,今天就来看看拥有几年c语言工作经验的大神整理的C语言入门基础知识,没有学不会,只有不肯学. 结构化程 ...

  3. 【无标题】C#nbsp;语言入门基础介绍学习通http://www.bdgxy.com/

    文章来源: 学习通http://www.bdgxy.com/ 普学网http://www.boxinghulanban.cn/ 智学网http://www.jaxp.net/ 表格制作excel教程h ...

  4. c语言入门自学ppt文库,《C语言入门基础》PPT课件.ppt

    第3章C语言入门基础 在正式学习C语言之前首先要掌握C语言的基本知识 如数制 对象的命名即标识符 以及C语言中的数据类型等 通过本章的学习为以后几章打下基础 3 1标识符 在C语言中 程序的编写是运用 ...

  5. Problem B: C语言习题 学生成绩输入和输出

    Problem B: C语言习题 学生成绩输入和输出 Description 编写一个函数print,打印一个学生的成绩数组,该数组中有5个学生的数据,每个学生的数据包括num(学号).name(姓名 ...

  6. Python数据结构与算法(1.3)——Python基础之输入、输出与高阶赋值

    Python数据结构与算法(1.3)--Python基础之输入.输出与高阶赋值 0. 学习目标 1. 输入.输出与注释 1.1 获取用户输入 1.2 格式化输出 1.2.1 基本方法 1.2.2 fo ...

  7. C语言入门基础知识有哪些?

    众所周知C语言经久不衰,并且很多人都想要成为C语言工程师,为了能够做好C语言技术学习,需要知晓C语言入门基础知识有哪些. 用一个简单的C程序例子,介绍C语言的基本构成.格式.以及良好的书写风格,使小伙 ...

  8. Go语言入门--基础语法

    Go语言入门系列文章目录 Go语言入门–基础语法 Go语言入门–流程语句 Go语言入门–数组和切片 集合(Map)(咕咕咕

  9. HTML网页设计语言入门基础教学视频-赖国荣-专题视频课程

    HTML网页设计语言入门基础教学视频-2858人已学习 课程介绍         本课程为软件相关专业网页设计入门基础课程,课程共18讲,上课代码及PPT课件已经上传,可以直接下载使用. 课程收益   ...

最新文章

  1. Github免费中文书《Go入门指南》,带你从零学Go | 极客头条
  2. 智能视觉组赛博 -10赛后反馈
  3. python语言怎么用-在python语言中,如何使用注释
  4. 中缀表达式转化为后缀表达式
  5. Windows Server 2003的功能级别
  6. 操作系统(五)输入/输出(I/O)管理
  7. 单行文字、多行文字溢出时省略号表示的多种解决方式;调整字符间距;段落首字母大写缩进效果;
  8. 沉得住气的程序员们!
  9. Mr.J--初识Ajax
  10. ASP.NET操作Word的IIS权限设置
  11. html5表单新功能解析,解析HTML5表单新功能-HTML5
  12. Linux socket can例程python版本
  13. mysql 堆表_Mysql聚集索引和非聚集索引(堆组织表和索引组织表)
  14. BZOJ4029: [HEOI2015]定价
  15. 显示屏色温调节 影响 测试软件,Twilight屏幕色温调节软件
  16. SpringMVC在返回JSON数据时出现406错误解决方案
  17. P1196 [NOI2002] 银河英雄传说 题解
  18. 如何用python批量处理图片大小_Python批量修改图片大小
  19. 是否应该使用utf-8 bom——因DirectVobSub不支持utf-8 no bom带来的问题
  20. 思科查看IP和MAC及交换机端口的命令

热门文章

  1. 参数化测试 junit_使用JUnitParams进行参数化的JUnit测试
  2. X-Mas Musings –在Grails集成测试中不要使用随机服务器端口
  3. java设计模式迭代器模式_迭代器模式和Java
  4. Apache Lucene 7.0即将发布!
  5. 使用Java Servlet,JSP标签和Stormpath快速构建Java Web App
  6. 将非事务性资源绑定到JTA事务中的几种模式
  7. 带有自定义模块的JBoss EAP上的骆驼
  8. MongoDB事实:商品硬件上每秒插入80000次以上
  9. 哪个内存更快?Heap或ByteBuffer或Direct?
  10. 3个简单步骤即可测试Java 8