C++ 文件读写方案选型

严格来说, 有 3 种风格.

  1. UNIX 底层读写库
  2. c 语言 stdio 标准库
  3. iostream 流

一般的工程中, 底层读写库封装程度太低, 需要自己处理缓存和很多通用的异常场景. 不适合.

网络编程中, 缓存会导致很多负面作用, 可以考虑用底层的读写库.

1. 格式化输出对比

1.1 格式化输出的可配置性

iostream 采用 manipulator 来格式化, 格式化信息写死在代码中, 难以实现可配置. -- 软件工程中绝对的硬伤.

stdio 采用的 %d %s 等已经发展成一个 DSL, 并在很多语言中通用. http://en.wikipedia.org/wiki/Printf_format_string#Programming_languages_with_printf

1.2 上下文无关

iostream 中, 通过改变流的状态来实现格式化输出, 而流的状态是全局的. 需要时刻小心,防止影响了后面的代码.

stdio 是上下文无关的.

1.3 缓冲区溢出

stdio 问题在于, 不是类型安全的, 而且存在缓冲区溢出的安全风险. 正确而安全的做法如下

#include <stdio.h>

int main()
{
    const int max = 80;
    char name[max];

    char fmt[10];
    sprintf(fmt, "%%%ds", max - 1);
    scanf(fmt, name);
    printf("%s\n", name);
}

1.4 类型安全与 64 位兼容性

stdio 的标准库, 需要明确知道待输出数据的类型, 并选择合适的 %d, %ld.

格式化字符串与参数类型不匹配会造成难以发现的 bug.

如果考虑到同时在 32 和 64 位机器上支持正确打印, %d 的选取更加头疼.

《The Linux Programming Interface》的作者建议(3.6.2节)先统一转换为 long 类型再用 "%ld" 来打印;对于某些类型仍然需要特殊处理,比如 off_t 的类型可能是 long long。

http://google-styleguide.googlecode.com/svn/trunk/cppguide.html#64-bit_Portability

printf 无法像打印 int 那样用 printf 来直接打印 自定义对象。

1.5 性能

  1. 即使只使用了解释程序的一个功能,也要全部装载。如上面的例子,我们要装载整个包,包括解释浮点数和字符串那部分程序段,无法减少程序的长度。  2. 虽然printf族函数已经优化得很好,但是,它是在运行期间进行解释,如果能在编译期间分析格式字符串里的变量,根据不同的类型调用各自的函数处理,运行会快得多,而且C++编译期间的类型检查会有助于我们发现错误.

在线 ACM/ICPC 判题网站上,如果一个简单的题目发生超时错误,那么把其中 iostream 的输入输出换成 stdio,有时就能过关。
  3. 对于C++来说,printf不能被扩展是它最大的缺点。我们不能通过重载函数对它进行扩展,因为重载函数要有不同类型的参数,而printf族函数把类型信息隐藏在可变参数表和格式字符串中。

2. 线程安全

stdio 的函数是线程安全的,

而且 C 语言还提供了 flockfile(3)/funlockfile(3) 之类的函数来明确控制 FILE* 的加锁与解锁。

cout << a << b; 是两次函数调用,相当于 cout.operator<<(a).operator<<(b)。

两次调用中间可能会被打断进行上下文切换,造成输出内容不连续,插入了其他线程打印的字符。

3. 内存泄露

 

posted @ 2014-10-22 01:46  Jackon Yang  阅读(456)  评论(2编辑  收藏  举报