Mac环境下C++ cin读取特定首字符单词时出现首字符截断错误
Mac系统下cin读取特定首字母字符串截断问题
问题现象
我在学习《Accelerated C++》、编译测试第4章的成绩统计程序时,发现cin无法正确读取以特定字符开头的姓名,运行示例如下:
$ echo "zach 1 1 1 fred 2 2 2 albert 3 3 3 james 4 4 4 adam 5 5 5" | ./main james 4 lbert 3 m 5 red 2 zach 1
从输出可以看到,以a、f这类字符开头的姓名会被误读,字符串开头至少被截断1个字符。
我试过复制网上公开的全章节习题参考答案代码编译运行,仍然存在相同问题。控制台打印结果显示,姓名在读取阶段就已经出现截断,完全无法定位问题产生原因。
这个问题仅在Mac系统下出现:同一份代码在Windows设备上编译运行可以得到正确输出,在另一台Mac设备上运行则会复现相同错误。
最小复现代码
以下是遵循书中代码规范编写的最小可复现示例(MRE),运行后会表现出相同异常:
#include <iostream> #include <vector> using std::cin; using std::cout; using std::endl; using std::istream; using std::string; using std::vector; double sum_vec(vector<double> &vec) { double total = 0; for (vector<double>::size_type i = 0; i < vec.size(); i++) { total += vec[i]; } return total; } istream &read_nums(istream &in, vector<double> &vec) { if (in) { vec.clear(); double x; while (in >> x) vec.push_back(x); in.clear(); } return in; } istream &read(istream &in) { string s; in >> s; vector<double> vec; read_nums(in, vec); double total = sum_vec(vec); cout << s << " " << total << endl; return in; } int main() { while (read(cin)) { } }
将上述代码编译为test可执行文件后,运行结果如下:
$ echo "albert 1 bruce 2 chris 3 dan 4 edward 5 fanny 6" | ./test albert 1 ruce 2 hris 3 4 0 ward 5 y 6 0
使用的编译命令为:
g++ -o test test.cpp
执行g++ --version查看编译器版本,输出如下:
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/c++/4.2.1 Apple clang version 12.0.0 (clang-1200.0.32.29) Target: x86_64-apple-darwin19.6.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin
问题根因
这是Mac系统自带老旧标准库的已知bug:
- 命令里调用的
g++实际是Apple clang的软链接,默认会链接苹果从GCC 4.2.1时代遗留的老旧libstdc++库。这个库的流读取逻辑存在缺陷:当流从读取数字的状态切换到读取字符串时,数字解析失败后的字符回退逻辑出错,会吞掉后续字符串的首字节。如果被吞掉的首字节是a、b、c、d、e、f这类十六进制合法字符,或者x、p、+、-、.这类浮点数解析的合法字符,就会触发你看到的截断现象。 - Windows下的MSVC标准库、Linux下的新版libstdc++/libc++都早已修复这个问题,所以不会复现。
修复方案
任选一种即可解决:
- 编译时指定链接苹果维护的新版
libc++库,在编译命令中添加-stdlib=libc++参数,完整命令参考:g++ -std=c++11 -stdlib=libc++ -o test test.cpp - 不要使用系统自带的老旧编译工具链,通过Homebrew安装最新版gcc或clang,用安装后的新版本编译代码即可。
内容的提问来源于stack exchange,提问作者James Kelleher
相关产品推荐
相关产品推荐

