0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

解析C语言入门基础之输入和输出

电子工程师 来源:编程学习总站 作者:写代码的牛顿 2021-05-31 14:05 次阅读

01

标准输入和标准输出

C语言里要使用标准输入和标准输出必须包含stdio.h头文件,常用的标准输出和标准输入函数是printf和scanf,其中printf用来在标准输出中输出信息,而函数scanf则用来从标准输入中读取信息。

那么什么是标准输入和标准输出呢?

Linux中进程通常会自动打开三个标准文件,即标准输入文件(stdin)通常对应文件描述符0;

标准输出文件(stdout)对应文件描述符1和标准错误输出文件对应文件描述符2(stderr)。进程将从标准输入文件中读取输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。02

标准输入函数

在stdio.h中scanf声明如下:

/* Read formatted input from stdin. This function is a possible cancellation point and therefore not marked 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表示输入一个单精度浮点数,其他数据类型的数据参考C语言入门基础之变量和数据类型,count保存scanf输入数据的有效数。

看到这里可能有人会有疑问,为什么调用scanf从标准输入信息,需要对变量取地址,为什么要设计成这样?这里就要涉及到后面会学到的知识:指针。在C语言里函数传参方式有2种,一种是传值另外一种是传指针。通过传值方式形参拷贝实参,得到一个实参副本对实参副本进行修改不会影响实参,而传指针方式,将会得到实参的地址,通过指针解引用可以间接修改实参的值。

那么回到scanf函数那里,我们通过对变量进行取址,scanf函数内部有一个指针,将变量地址值赋给内部指针,再将标准输入的值赋值给实参,实参变量因此获得标准输入的值。

在代码片段我们还看到scanf_s这个函数(scanf_s不是C标准库函数),由于scanf函数并不是安全的,在有些编辑器上默认禁止使用scanf,如果使用则需要打开一个宏,而scanf_s是一些厂商提供的scanf函数安全版本,两者使用方法一模一样。

03

标准输出函数

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

/* Write formatted output to stdout. This function is a possible cancellation point and therefore not marked 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 ”, num); printf(“output_count = %d ”, output_count); output_count = printf(“f_num = %f ”, f_num); printf(“output_count = %d ”, output_count);

在代码片段里我们看到一个 字符,在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绑定。

04

结语

后面讲解C语言知识时我会穿插有Linux相关知识,讲解C语言不能仅仅停留在语法层面。据我的观察,很多人学习了C语言语法后很迷茫,不知道C语言能做什么,根本原因就是你没有了解某个平台的系统编程API。Linux是一个开源操作系统,结合Linux学习C语言将会更加有趣,在Linux上进行C语言开发绝对是最佳选择。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11222

    浏览量

    208886
  • C语言
    +关注

    关注

    180

    文章

    7597

    浏览量

    136130
  • File
    +关注

    关注

    0

    文章

    19

    浏览量

    14322
  • 函数
    +关注

    关注

    3

    文章

    4304

    浏览量

    62416
  • 代码
    +关注

    关注

    30

    文章

    4742

    浏览量

    68330

原文标题:C语言入门基础之输入和输出

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FPGA编程语言入门教程

    FPGA(现场可编程逻辑门阵列)的编程涉及特定的硬件描述语言(HDL),其中Verilog和VHDL是最常用的两种。以下是一个FPGA编程语言(以Verilog为例)的入门教程: 一、Verilog
    的头像 发表于 10-25 09:21 143次阅读

    TMS320LF240x DSP的C语言和汇编代码快速入门

    电子发烧友网站提供《TMS320LF240x DSP的C语言和汇编代码快速入门.pdf》资料免费下载
    发表于 10-18 10:14 0次下载
    TMS320LF240x DSP的<b class='flag-5'>C</b><b class='flag-5'>语言</b>和汇编代码快速<b class='flag-5'>入门</b>

    模拟DAC38RF8x输入/输出缓冲器信息的快速入门方法

    电子发烧友网站提供《模拟DAC38RF8x输入/输出缓冲器信息的快速入门方法.pdf》资料免费下载
    发表于 10-09 11:21 0次下载
    模拟DAC38RF8x<b class='flag-5'>输入</b>/<b class='flag-5'>输出</b>缓冲器信息的快速<b class='flag-5'>入门</b>方法

    技术干货驿站 ▏深入理解C语言:编程高手必备,全方位解析运算符的核心技能!

    过程中发挥着重要作用,还对程序的性能和稳定性产生直接影响。继上一期对C语言常量的深入分析后,本文将进一步探讨C语言中的常用运算符,详细解析
    的头像 发表于 09-18 15:56 307次阅读
    技术干货驿站 ▏深入理解<b class='flag-5'>C</b><b class='flag-5'>语言</b>:编程高手必备,全方位<b class='flag-5'>解析</b>运算符的核心技能!

    误差放大器的输入输出关系

    误差放大器(Error Amplifier)在电子测量和控制系统中扮演着至关重要的角色,其输入输出关系对于理解和设计这些系统至关重要。以下是对误差放大器输入输出关系的详细解析,包括其工作原理、
    的头像 发表于 09-11 15:32 937次阅读

    C语言中的输入&amp;输出

    int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出
    发表于 02-28 11:17 458次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>中的<b class='flag-5'>输入</b>&amp;<b class='flag-5'>输出</b>

    ArkTS语言基础类库-解析

    被设计用来传输和存储数据,是一种可扩展标记语言语言基础类库提供了[XML生成、解析与转换]的能力。 URL、URI构造和解析能力:其中[URI]是统一资源标识符,可以唯一标识一个资源
    发表于 02-20 16:44

    plc编程语言c语言的联系 c语言和PLC有什么区别

    PLC编程语言C语言的联系 PLC(可编程逻辑控制器)是一种针对自动化控制系统的特殊计算机。PLC编程语言是为了控制和管理自动化生产过程中的各种设备而设计的。与
    的头像 发表于 02-05 14:21 3848次阅读

    c语言,c++,java,python区别

    操作系统、嵌入式系统等对性能要求较高的场景。C语言的语法相对简单,学习曲线较平缓,也是学习其他高级语言入门语言
    的头像 发表于 02-05 14:11 2227次阅读

    vb语言c++语言的区别

    Microsoft开发的一种面向对象的事件驱动编程语言。它的设计目标是简化编程过程,让初学者也能快速上手。与相比,C++语言是一种通用的、面向对象的编程
    的头像 发表于 02-01 10:20 2048次阅读

    C语言数据结构跳表详解

    大家好,今天分享一篇C语言数据结构相关的文章--跳表。
    的头像 发表于 12-29 09:32 791次阅读
    <b class='flag-5'>C</b><b class='flag-5'>语言</b>数据结构<b class='flag-5'>之</b>跳表详解

    C语言构建高效的嵌入式程序

    嵌入式工程师在编写C语言程序时,需要注重效率和清晰的思路。本文将通过解析经典问题“猴子选大王”来展示如何用C语言思维方式构建高效、清晰的程序
    的头像 发表于 12-21 09:27 595次阅读

    c语言怎么输出ascii码对应的字符

    行字符的存储和处理。本文将介绍如何在C语言输出ASCII码对应的字符。 首先,让我们来了解一下ASCII码的基本概念。ASCII码由美国国家标准协会(ANSI)于1963年制定,在计算机系统中广泛使用。它将常见字符(如大写字母
    的头像 发表于 11-26 10:32 9188次阅读

    c语言gets函数可以输入数字吗

    C语言中的gets函数是用来读取字符串的,而不是用来读取数字的。它会读取输入的字符直到遇到换行符或者文件结束符。因此,如果你尝试使用gets函数来读取数字,是无法准确获取数字本身的。 首先,让我们
    的头像 发表于 11-24 10:00 1451次阅读

    c语言printf输出空格间隔

    C语言中,需要在printf输出中添加空格间隔可以通过多种方式实现。这个空格间隔可以用于对输出进行格式化使其更加易读、美观。在接下来的文章中,我将详细介绍如何使用空格来间隔
    的头像 发表于 11-24 09:56 6197次阅读