深入 C 语言和程序运行原理
于航
PayPal 技术专家
21121 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
深入 C 语言和程序运行原理
15
15
1.0x
00:00/00:00
登录|注册

11|标准库:深入理解标准 IO

Canary
ASLR
DEP
标准未明确规定,通常为 1
将字符压回到输入流
安全性技术
fgets
已在 C99 弃用,C11 移除
无边界检查
rdi, rsi, rdx, r10, r8, r9
setvbuf
fflush
标准 IO 间接使用
低级 IO 直接调用
标准 IO 更抽象
低级 IO 更细节
示例: stdio.h
可移植性强
抽象通用接口
示例: POSIX 接口
使用操作系统底层接口
依赖于具体操作系统
标准 IO 的安全性考量
系统调用的机制与使用
缓冲 IO 的优势与实现
IO 接口的不同层级及其特点
C 标准库与系统调用的依赖关系
ungetc 连续调用次数
ungetc 函数作用
替代方案
gets 函数
返回值通过 rax 寄存器
使用寄存器传递参数
通过 syscall 指令执行
操作系统提供的函数
可以指定缓冲策略
缓冲区大小由标准库决定
提升性能
减少系统调用次数
系统调用
接口使用粒度
标准 IO
低级 IO
总结
思考题
安全性问题
系统调用
带缓冲的标准 IO
标准 IO 与低级 IO 的区别
IO 接口级别
C 语言标准 IO 模型

该思维导图由 AI 生成,仅供参考

你好,我是于航。
输入输出(后面简称 “IO”)是应用程序不可或缺的一种基本能力。为了保持设计上的精简,C 语言并没有在核心语言层面提供对 IO 相关接口的支持,相反,采用了标准库的方式来实现。通过引用名为 stdio.h 的标准库头文件,我们便可以快捷地为 C 程序添加读取用户键盘输入、输出内容到控制台,乃至读写文件等一系列常规的 IO 功能。
这一讲,我将为你深入介绍 C 语言中的标准 IO 模型,以及它背后的一些原理。

快速回顾 IO 接口的使用方法

首先,让我们通过下面这段代码来快速回顾,应该如何在 C 语言中使用这些由标准库提供的 IO 接口。对于这些接口用法的更具体说明,你可以参考这个链接
#include <stdio.h>
int main(void) {
printf("Enter some characters:\n");
FILE* fp = fopen("./temp.txt", "w+");
if (fp) {
char ch;
while (scanf("%c", &ch)) {
if (ch == 'z') break;
putc(ch, fp);
}
} else {
perror("File open failed.");
}
fclose(fp);
return 0;
}
这里,在 main 函数内部,我们通过多种不同的方式,让程序与进程预设的 IO 流以及我们自行打开的 IO 流产生了交互。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C 语言标准 IO 模型是应用程序中不可或缺的基本能力。文章深入介绍了标准 IO 模型及其背后的原理。首先回顾了标准库提供的 IO 接口的使用方法,然后对比了标准 IO 和低级 IO 接口的不同级别。标准 IO 提供了带缓冲的输入输出操作,通过缓冲区减少低级 IO 接口的调用次数,提升了性能。文章还提到了标准 IO 接口提供了自由使用不同缓冲策略的能力,可以通过接口如 fflush 和 setvbuf 来控制缓冲区的使用。总的来说,标准 IO 模型在接口设计与使用方式上更加统一和通用,能够屏蔽底层不同系统的实现细节,同时提供了性能优化的机制。 文章还介绍了低级 IO 的相关实现细节,包括系统调用的机制和使用方式。通过实际代码展示了在机器指令层面使用系统调用的过程,深入解释了系统调用函数的调用过程和参数传递方式。 此外,文章还警示了C语言标准IO接口中存在的安全隐患,特别是对于 gets 函数的使用。强调了 gets 函数的不安全性,并指出了主流编译器对其的弃用和相关安全技术的应用。 总的来说,本文通过深入解析C语言标准IO模型及其底层实现细节,以及对安全性的警示,为读者提供了全面的技术视角和实用建议。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入 C 语言和程序运行原理》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(12)

  • 最新
  • 精选
  • liu_liu
    setvbuf(fp, buf, _IOFBF, 5); 设置了缓冲区的大小为 5。表示每输入 5 个字符,就会写入文件。_IOFBF 表示 fully buffer。 而未使用 setvbuf 时,经测试,默认缓冲区大小是 4kb,当在输入 4k 个字符后,才会写入文件。

    作者回复: 正解!

    2022-01-05
    12
  • liu_liu
    ungetc 用于向流里面放回字符,取出字符的顺序与放回字符的顺序相反。 比如放回的顺序如下: ungetc ('d', file); ungetc ('c', file); ungetc ('b', file); ungetc ('a', file); 那么取出时,依次是 a、b、c、d。 在网上查资料说,ungetc 不同平台实现可能不一样,有的说调用多次可能会失败,只保证一次的放回。 我在 unbuntu 64 位 下测试,多次调用是没问题的,貌似可放回的字符数是有个缓冲区大小的。 请老师解答下~

    作者回复: 很棒!实践出真知!实际上,标准中对 ungetc 的规定是,一个大小为 1 字节的“回退 buffer”是可以被保证的,但是多于一次的连续回退操作能否成功,则完全取决于具体实现。通常来说在实践中,这个 buffer 的大小最小只有标准规定的 1 字节,最大甚至可以达到 4k 字节大小。

    2022-01-05
    2
    7
  • ppd0705
    我记得 APUE 上说缓冲有三种类型:全缓冲、行缓冲、无缓冲。看来写入普通文件,标准 IO 默认是全缓冲,低级 IO 是行缓冲?

    作者回复: 这个是不一定的,比如在标准 IO 里,标准错误输出肯定不是全缓冲的。相应的,对于低级 IO 来说,大多数方法都是无缓冲的,但具体使用之前还是最好看一下 man 文档,以防万一。

    2022-03-14
    3
  • ZR2021
    讲的太好了,尤其是那个内嵌汇编,眼前一亮的感觉!!!不过老师,还是有几个问题想请教下您: 1. 系统调用传参使用的是寄存器,不管参数是值还是地址,传地址的话,底层会调用拷贝函数进行拷贝,那如果是结构体类型的值传参要怎么办,还是说不能有这种传参方式的? 2. 系统调用传参就那么几个寄存器传参,传参个数超过了怎么办呢?还是也被规定了不能超过6个参数的,貌似我确实也没见过超过6个参数的系统调用…… 3.这种汇编内嵌的方式感觉很厉害的样子,之前一直想学下就是学不好,后面有没有相应的课程讲解的,讲下常见的要点什么的…… 还有,这种内嵌汇编方式的代码效率比c/c++高的吗,如果高的话高在哪里呢?还是只是为了做一些高级语言做不到的事情? 期待老师的解惑,可能有些问题比较低级……

    作者回复: 感谢认可,都是很好的问题!下面是回答: 1. 在 Linux x86-64 系统调用中,有一些是需要结构体对象作为参数的,比如 sys_readv。但传递方式都是通过指针,不会有直接按值传递结构体对象的情况。 2. 在 Linux x86-64 系统调用中,是没有需要通过栈来传参的系统调用的。 3. 我们在后面的课程中还会用到,但不会有专门的章节介绍哈。实际上对于大部分手写汇编,执行效率都是没有编译器产出的汇编高的,所以在日常的 C 编程中并不常用。并且由于内联汇编也会有兼容性问题,包括 asm 关键字的用法我印象里不同编译器的支持程度都是不同的。当然如果需要使用,主要关注具体汇编指令的用法,以及 asm 关键字的用法就可以。前者可以参考具体平台上的具体 manual,后者以 GCC 为例,可以参考:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

    2022-01-08
    3
    3
  • pedro
    老师,有没有啥 gets 的替代品?

    作者回复: 可以使用 fgets 函数哈。

    2022-01-05
    2
    3
  • may_huang
    > cat my_fopen.c #include <stdio.h> int main(void) { printf("Enter some characters:\n"); FILE* fp = fopen("./temp.txt", "w+"); if (fp) { char ch; while (scanf("%c", &ch)) { if (ch == 'z') break; putc(ch, fp); } } else { perror("File open failed."); } fclose(fp); return 0; } seroiuts01390> gcc my_fopen.c # /workspace/git/ehuamay/test seroiuts01390> ./a.out Enter some characters: File open failed.: Permission denied Segmentation fault 为啥在我的环境里运行会coredump?

    作者回复: 看起来是因为没有权限 “Permission denied”。SF 可能是因为文件没有被打开,却执行了 “fclose(fp);”。

    2022-08-03归属地:北京
    1
  • ppd0705
    老师,请问 "C 语言提供的 IO 接口属于“标准 IO”的范畴。" 这句话可以理解为 stdid 库提供的接口属于标准 IO 范畴吗?不然 fcntl 不也算 C 语言提供的一个标准库吗?

    作者回复: 标准 IO 实际上是特指由 ISO C 标准定义的一系列接口,这些接口被定义在 C 标准中。这里你提到的 fcntl 只能算是 POSIX.1-2001 标准中的接口。

    2022-03-15
    2
    1
  • Jack
    系统调用对应的 ID,去哪里找?

    作者回复: Linux x86-64 可以在这边查哈:https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

    2022-01-09
    1
  • Geek__38012c6589d3
    这节讲的真好 于老师牛逼

    作者回复: 感谢认可!^~^

    2022-02-08
  • 老师,请问这一篇用到的内联汇编的语法,有没有什么文档可以查阅的

    作者回复: 有的,可以参考这个文档:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

    2022-02-08
收起评论
显示
设置
留言
12
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部