【C++】浅论(cin和cout)的解锁、缓冲区的理解、CC++输入方法汇总和详解

一、cin,cout解锁

1.1:cin,cout解锁以及why

首先cin和cout是在c++中为了提供类型安全易用性设计的,它兼容了c语言的输入和输出,以上几点导致它在性能行(读取和输出速度)远不如传统c语言的输入和输出。

在看到一些代码里面,会在main函数开头加上这两行代码,叫做cincout解锁,使用之后确实能对性能有一定的提升:

ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);

🚨注意:cin cout解锁使用时,不能与 scanf,getchar, printf,cin.getline()混用,一定要注意,会出错。

我们来看看这两行代码其实是在做什么,但在这之前,让我们先学习一下底层的一些知识:输入输出缓冲区。

二:C++输入输出缓冲区

ios::sync_with_stdio(false);首先,上文提到过,由于cin、cout是兼容了C语言的输入和输出,这意味着C++的IO标准库和C的IO标准库是同步的,这能保证可以在程序中混用cin、coutscanf、printf。但是这种同步带来的代价是,在用户进行输入和输出时,这两个库之间会协调同一片共享缓冲区,保证程序能按顺序的执行,这种协调会导致资源的消耗。这个代码相当于关闭了这种协调机制,减少了这部分的资源消耗,但是同时你也就不能混用两种输入输出方式!
cin.tie(0),cout.tie(0): 首先我们需要明白,在C++的输入和输出过程中有两个独立的缓冲区:输入缓冲区、输出缓冲区。

在这里插入图片描述

  1. 输入缓冲区:当我们用标准输入从外部设备(如键盘or硬盘)向程序(也就是内存)输入数据时,数据不会立马跑到程序里被程序的变量所接收从而处理,而是先到内存的输入缓冲区,等到 cin函数进行缓冲区数据读取时,数据才会从缓冲区这个中间站流向内存
  2. 输出缓冲区: 它和输入缓冲区的性质是一样的,区别在于它数据的流向:从内存流向设备(硬盘、显示屏);和cin输入数据一样,当我们用cout将数据从程序(内存)往外部设备输出时,它不会直接输出,而是先将数据送到缓冲区(打住,cout的作用就是这个),当某些条件触发(如:缓冲区满了、缓冲区被刷新),才会将在缓冲区的数据送到外部设备。

 而C++默认cincout是绑定的,也就是说,当我们用cin输入数据时,它会自动提前刷新输出缓冲区,以确保将之前就被拿出来的数据能输出。而这种绑定机制可能会频繁无意义的多次刷新缓冲区,造成资源浪费。而cin.tie(0),cout.tie(0)的作用就是解除这种绑定机制,谁也不用等谁。但是这样带来的问题是:在进行输入之前,必须手动刷新缓冲区,否则前面的提示信息还没显示,后面的输入先来了。
在这里插入图片描述

 关于上面的手动刷新缓冲区,意思就是说,你需要手动把cout从内存拿到缓冲区的数据输出到屏幕上,否则它就一直在在缓冲区,除非手动(<<flush或<<endl)刷新才能显示。这里举个例子:

int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);int n = 0;cout << "请输入棋盘(nxn)的大小:n = ";cin >> n;return 0;
}

这个代码,按理来说,应该是先出现提示信息,再进行输入n,但是实际运行结果是:

在这里插入图片描述
我们看到,是先输入,再输出了本该提前输出的提示信息。这是因为我们对cincout进行解绑后,cout不再具有在cin运行前自动刷新输出缓冲区(意味着将缓冲区的数据输出到屏幕)的功能,所以其实在输入2之前,cout已经将提升信息从程序(内存)拿到了缓冲区,但是没有将其刷新送到屏幕,而是在程序结束后不得不刷新缓冲区。

所以,在对cincout进行解绑后,一定要记得手动刷新缓冲区(flushendl):

cout << "请输入棋盘(nxn)的大小:n = " << flush;
cin >> n;
cout << "请输入棋盘(nxn)的大小:n = " << endl;
cin >> n;

三、C的标准输入

虽然在某些情况下,使用cincout解绑后能提升性能,但是肯定还是没有用传统的c语言的输入输出效率和性能高。所以我打算法竞赛的朋友也建议我用传统的输入输出方法,下面也在这里做一个简单的总结:

3.1:scanf和printf

  • 定义:按照特定格式从stdin读取输入。

  • 用法示例:

    char str[100];
    int a;
    scanf("%s %d", str, &a);    // 注意,传入的一定是变量的地址
    
  • 从缓冲区读取数据流程:

    • 缓冲区开头:读取并丢弃空白字符(包括空格、Tab、换行符),直到第一个非空白字符才认为是第一个数据的开始。
    • 缓冲区中间:开始读取第一个数据后,一旦遇到空白字符(非换行符), 就认为读取完毕一次。遇到的空白字符不读取残留在缓冲区,直到下一次被读取或刷新。例如输入字符串hello yyz,则会被认为是2个字符串。
    • 缓冲区末尾:按下回车键时,换行符\n残留在缓冲区。换行符之前的空格可以认为是中间的空白字符,处理同上。
    char c[100]; // 分配长度为100的字符数组scanf("%s", c); // 不需要使用&,因为数组名已经是地址printf("%s", c);
    

    在这里插入图片描述

  • 格式控制符说明:
    在这里插入图片描述

注意,%c是一个比较特殊的格式符号,它将会读取所有空白字符,包括缓冲区开头的空格、Tab、换行符,使用时要特别注意。

3.2:字符和字符串(char [])

3.2.1:fgetc() & getc()读取字符

  • 定义:从指定输入流读取一个字符,输入可以是 stdin,也可以是文件流 ,使用时需要显式指定。

    这两个函数完全等效,getc()由fgetc()宏定义而来。不同的是,gets()和fgets()相互之间没有关系。

    fgetc()和getc()对应的输出函数是fputc()putc()

  • 用法示例:

    char a, b;
    a = fgetc(stdin);
    b = getc(stdin);
    

    在下面代码中,如果输入一个字符按下回车,那么a读取到的是第一个字符,而b读取到的是紧跟着的回车

    char a, b;
    a = fgetc(stdin); // 从标准输入读取一个字符
    b = getc(stdin);  // 再从标准输入读取另一个字符fputc(a, stdout); // 输出字符a到标准输出
    putc(b, stdout);  // 输出字符b到标准输出
    

    在这里插入图片描述

  • 读取数据流程:
    所有空格、Tab、换行等空白字符,无论在缓冲区开头、中间还是结尾,均会被读取,不丢弃也不会残留在缓冲区
    如果输入一个字符如’a’,然后按下回车键,则读取到的是字符’a’,同时换行符\n残留在缓冲区
     因为只读取一个字符,所以如果输入多于1个字符(包括换行符),则它们均会残留在缓冲区。具体地说,如果什么字符都不输入,直接按下回车键,则读取到的是换行符\n,缓冲区无任何残留;

3.2.2:getchar()读取字符

  • 定义:从stdin读取一个字符。

    getchar()实际上也由fgetc()宏定义而来,只是默认输入流为stdin。( 它和前面两个唯一不同的就是不需要显示指定输入流

    getchar()对应的输出函数是putchar()

  • 用法示例:

    char a;
    a = getchar();
    

    getchar()常常用于清理缓冲区开头残留的换行符。当知道缓冲区开头有\n残留时,可以调用getchar()但不赋值给任何变量,即可实现冲刷掉\n的效果。

3.2.3:字符串读入——fgets()

  • 定义: char *fgets(char *str, int n, FILE *stream)从指定输入流读取一行,输入可以是stdin,也可以是文件流,使用时需要显式指定, 并把它存储在 str 所指向的字符串内。当读取(n-1)个字符时,或者读取到换行符\n时,或者到达文件末尾时,它会停止读取,并在末尾补上\0

    • str – 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
    • n – 这是要读取的最大字符数(包括最后的空字符\0)。通常是使用以 str 传递的数组长度。(当输入的字符小于最大个数n-1时,会读取到\n并停止,\n会被读取到,不会丢弃,和普通字符地位一样)
    • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。
  • 读取文件流示例:

char str[100];
memset(str, 0, sizeof(str));
int i = 1;FILE *fp = fopen("...test.txt", "r");
if (fp == NULL) {printf("File open Error!\n");exit(1);
}while (fgets(str, sizeof(str), fp) != NULL)printf("line%d [len %d]: %s", i++, strlen(str), str);fclose(fp);
  • 读取stdin示例:
char str[100];
memset(str, 0, sizeof(str));
int i = 1;
while (fgets(str, sizeof(str), stdin) != NULL)printf("line%d [len %d]: %s", i++, strlen(str), str);
  • 读取数据流程:
    • 所有空格、Tab等空白字符均被读取,不忽略。
    • 按下回车键时,缓冲区末尾的换行符也被读取,字符串末尾将有一个换行符\n作为字符串的一部分
    • fgets()函数会自动在字符串末尾加上\0结束符。所以当输入字符串hello,再按下回车,则读到的字符串长度为6(hello + \n),字符数组大小为7(因为包括了\0)
    • 第 2 个参数n指定了读取的最大长度(算上\0的)。函数读到n-1个字符(包括换行符\n)就会停止,并在末尾加上\0结束符。剩余字符将残留在缓冲区。

四、C++标准输入

4.1:cin和cout

  • 定义:cin是 C++ 的标准输入流对象,即istream类的一个对象实例。cin有自己的缓冲区,但默认情况下是与stdin同步的,因此在 C++ 中可以混用 C++ 和 C 风格的输入输出(在不手动取消同步的情况下)。

    cin与stdin一样是行缓冲,即遇到换行符时才会将数据同步到输入缓冲区。

  • 读取数据流程:cin对空白字符的处理与scanf一致。即:跳过开头空白字符,遇到空白字符停止读取,且空白字符(包括换行符)残留在缓冲区

🚨注意:所以cin用来读取字符串是读取不到空白字符的!!!字符串string的读取需要用到下面专门读取字符串的函数!!!!

4.2:字符串读取

4.2.1:给C类型字符串(字符数组 char [])赋值

1:cin.get()
  • 定义:
  • 读取单个或指定长度的字符,包括空白字符
  • 当使用无参数时,它读取并返回下一个字符
  • 也可以与一个参数一起使用(字符的引用),用来读取一个字符,包括换行符\n
  • 当与两个参数一起使用时(字符数组和长度)std::cin.get(buffer, SIZE);
    • 它会读取至多 SIZE - 1 个字符到 buffer 中(SIZE 是数组大小的参数)。这是因为最后一个位置需要留给空字符(null character,'\0'),这是 C 风格字符串的结束标志。
    • 如果在读取 SIZE - 1 个字符之前遇到了文件结束符(EOF,通常由用户输入 Ctrl+D 或 Ctrl+Z 产生),或者遇到了换行符 '\n',std::cin.get() 就会停止读取。
    • 如果在读取 SIZE - 1 个字符之前遇到了换行符 '\n'遇到换行符\n,随之停止读取;换行符\n留在输入缓冲区
  • 用法示例:
#include <iostream>int main() {const int SIZE = 10;char buffer[SIZE];std::cout << "Enter some text: ";std::cin.get(buffer, SIZE);// 注意:手动添加 null 字符来确保字符串正确终止buffer[SIZE - 1] = '\0';std::cout << "You entered: " << buffer << std::endl;// 如果需要清理输入流if (std::cin.peek() == '\n') {// 如果下一个字符是换行符,只需忽略它std::cin.ignore();} else {// 清除剩余的输入直到下一个换行符std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');}return 0;
}
  • cin.get()读取单个字符时,类似于 C 中的fgetc(),对空白字符的处理也与其一致,可以读取回车\ncin.get()读取的字符也可以赋值给整型变量。
  • cin.get()读取指定长度个字符时,类似于 C 中的fgets(),但在换行符的处理上不同:fgets()会将缓冲区末尾的换行符\n也写入字符串,而cin.get()遇到缓冲区末尾的\n,立即停止读入,\n会被残留在缓冲区。即:当输入test时,用fgets()读取得到的字符串长度为5(把\n读入),用cin.get()读取得到的字符串长度为4。
2.cin.getline()
  • 定义:读取指定长度的字符,包括空白字符。
  • 用法示例:
    char str[20];
    cin.getline(str, sizeof(str));    // 第3个参数也可以指定终止字符
    
    cin.getline()cin.get()指定读取长度时的用法几乎一样。
    唯一区别在于,
    • cin.getline()读取到\n会停止读取,但不会将\0追加到字符串末尾而是直接将其丢弃,所以不同于cin.get() cin.getline()不会把\n残留在输入缓冲区。
    • 如果输入的字符个数大于指定的最大长度n-1(不含终止符),cin.get()会使余下字符残留在缓冲区,等待下次读取;而cin.getline()会给输入流设为 Fail 状态,在主动恢复之前,无法再进行正常输入。

4.2.2:给string类型赋值:getline()

  • 定义:getline()并不是标准输入流istream的函数,而是字符串流sstream的函数,只能用于读取数据给string类对象,使用时也需要包含头文件<string>

    如果使用getline()读取标准输入流的数据,需要显式指定输入流。

  • 用法示例:

    int n;
    string s;
    cin >> n;
    getchar(); //cin.get() 清空前面cin留下的换行符,避免下面读到空字符串
    getline(cin, s);//可正确读入下一行的输入
    
  • 和前面cin.get()函数不同的是,它是 遇到换行符\n读取并丢弃,随之停止读取;换行符\n既不加到字符串末尾,也不会留在输入缓冲区(这点和cin.getline()是一样的)

  • 需要注意的是,假如缓冲区开头就是换行符(比如可能是上一次cin残留的),则getline()会直接读取到空字符串并结束,不会给键盘输入的机会。所以这种情况下要注意先清除开头的换行符

五、C&C++的输入方法总结

 首先在C语言中

  • scanf用来格式化读取各种基本数据(但是遇到空比字符则视为读入的终止标志而停止,所以不可用来读取带有空格的字符串)
  • getchar()fgets()用来读取字符(可读白字符:如空格、换行),区别仅在于getchar()默认是stdin输入流,不用显示传参
  • 在读取字符串时(C语言格式的字符数组char []),使用fgets读入,其会读取到换行符\0便终止读取,并把\0算入字符串的一个字符加入到字符串末尾,并且自动在字符串末尾加上\0

 在C++中

  • cin可以用来读取各种基本数据,和scanf一样即:跳过开头空白字符,遇到空白字符停止读取,且空白字符(包括换行符)残留在缓冲区。所以不可用来直接读取带有空格的字符串
  • cin.get()cin.getline()用法方面类似,都是对传统C形式字符串即字符数组char []的读取(cin.get()在读取字符时和putchar()用法一致)。二者而和fgets()不同的是,这两者都不会将\0作为字符串的一部分追加到字符串末尾;但cin.get()的做法是直接将\0残留在了缓冲区,而cin.getline()是将其读取并且丢弃,输入缓冲区不会残留。同时它们也会自动给字符数组末尾追加\0,即仅从输入流中读取 n-1个字符,字符数组末尾会用 '\0'补充
  • getline()是专门对string这种字符串类型变量的读取,它对换行符的处理和上面的cin.getline()一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/16872.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 脚本化 Git 操作:简单、高效、无压力

前言 如何判定此次测试是否达标&#xff0c;代码覆盖率是衡量的标准之一。前段时间&#xff0c;利用fastapi框架重写了覆盖率统计服务&#xff0c;核心其实就是先获取全量代码覆盖率&#xff0c;然后通过diff操作统计增量代码覆盖率&#xff0c;当然要使用diff操作&#xff0c…

Java中Stack的使用详解

Stack是一种运算受限的线性表&#xff0c;其特点在于仅允许在表的一端&#xff08;即表尾&#xff09;进行插入和删除操作。这一端被称为栈顶&#xff0c;而相对的另一端则称为栈底。向一个栈插入新元素的操作称为进栈或入栈&#xff0c;它将新元素放到栈顶元素的上面&#xff…

从杂乱无章到井井有条——五款笔记软件,重塑工作与生活

记得刚入职场那会&#xff0c;我总是被各种繁杂的信息和任务搞得焦头烂额。会议记录、项目计划、灵感闪现……这些都需要我随时记录和整理。 然而&#xff0c;我的桌面总是堆满了便签纸和草稿本&#xff0c;手机相册里充斥着各种截图和备忘录&#xff0c;每次需要查找资料都像…

【数据结构】红黑树——领略天才的想法

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 目录 二叉搜索树 AVL树 红黑树概述 性质详解 效率对比 旋转操作 元素操作 代码实现 二叉搜索树 【数据结构】二叉搜索树-CSDN博客 AVL树 【数据结构】AVL树——平衡二叉搜索…

深度学习实战-yolox训练ExDark数据集(附全过程代码,超详细教程,无坑!)

跳转:数据集获取以及前期准备工作 本人在深度学习实战-yolov5训练ExDark数据集(附全过程代码,超详细教程,无坑!)的数据基础上实现yolox的训练,所以先跳转到该文章下去获取数据集,再继续接下来操作过程。 一、VOC格式数据集制作 1.前期工作 2.转变成voc格式 在datase…

Latex:newcommand

参考文献&#xff1a; latex中自定义的命令———\newcommand-CSDN博客LaTeX技巧924&#xff1a;详解newcommand的参数和默认值 - LaTeX工作室 (latexstudio.net) 文章目录 (re)newcommand自定义的一些命令 (re)newcommand ”定义命令“ 的定义&#xff1a; \newcommand{<…

[每周一更]-(第98期):PHP版本的升级历程

文章目录 大致历程PHP/FI (PHP 1.0)PHP 2.0PHP 3.0PHP 4.0PHP 5.0PHP 5.3 - 5.6PHP 7.0PHP 7.1 - 7.4PHP 8.0PHP 8.1 - 8.2 参考 PHP&#xff0c;即“超文本预处理器”&#xff08;Hypertext Preprocessor&#xff09;&#xff0c;是广泛应用于web开发的服务器端脚本语言。自19…

什么是独特摆动交易策略?fpmarkets1分钟讲清楚

摆动交易策略想必各位投资者都已经接触过了&#xff0c;但是什么是独特摆动交易策略&#xff1f;各位投资者知道吗&#xff1f;其实很简单&#xff0c;这是一种基于斐波纳契工具的独特摆动交易策略。下面fpmarkets1分钟讲清楚&#xff0c;趋势总会经历调整&#xff0c;而这些调…

【机器学习】Python中的决策树算法探索

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Python中的决策树算法探索引言1. 决策树基础理论1.1 算法概述1.2 构建过程 2. P…

数据集003:猫类识别-12种猫分类数据集 (含数据集下载链接)

数据集简介&#xff1a; 训练集共有2160张猫的图片, 分为12类. train_list.txt是其标注文件 测试集共有240张猫的图片. 不含标注信息. 训练集图像&#xff08;部分&#xff09; 验证集图像&#xff08;部分&#xff09; 标签 部分代码&#xff1a; # 定义训练数据集 class T…

eNSP华为模拟器-DHCP配置

拓扑图 要求 PC1通过DHCP获取192.168.1.1地址PC2和PC3通过DHCP接口地址池方式获取IP地址配置静态路由使其ping通 配置 配置主机名及接口IP地址 # AR1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sys AR1 [AR1]int g0/0/0 [AR1-Gigabit…

去重复记录和排序——kettle开发09

一、去除重复记录 去除重复记录&#xff0c;就是将数据流中的数据进行字段比较&#xff0c;从而去掉重复值的过程。去除重复记录的前提是需要将数据流中的数据进行排序&#xff0c;然后再进行去重操作。 去除重复记录的逻辑是&#xff0c;如下图&#xff0c;我们将需要比较的…

基础使用-SQL-图形化界面工具DataGrip

一、连接mysql &#xff08;1&#xff09;选择加号&#xff0c;再选择添加一个数据源&#xff08;Data Source&#xff09;&#xff0c;然后选择MySQL &#xff08;2&#xff09;接下来就需要去配置MySQL的连接信息&#xff0c;并且去下载它的驱动&#xff0c;安装驱动时可能要…

微信公众号怎么做留言板功能

在繁忙的都市生活中&#xff0c;你是否常常感到孤单、渴望有一个可以倾诉心声的地方&#xff1f;今天&#xff0c;我要为大家介绍一个特别的角落——我们公众号的留言板功能。它不仅是一个留言板&#xff0c;更是一个情感交流的平台&#xff0c;一个可以让我们彼此心灵相通的桥…

百度发布代码辅助工具,超强

不会用AI的程序员&#xff0c;会跟不会用智能手机的人一样 百度这个代码助手助手感觉还是不错的 https://comate.baidu.com/?inviteCodeijmce7dj 目前看下来这个代码助手是比较强的&#xff0c;比阿里的那个灵码好用&#xff0c;他可以引用到当前的文件&#xff0c;并且能分…

idea改了代码,但是需要紧急切换分支,需要把改动的保存到本地

但是如果有冲突&#xff0c;你没有合并&#xff0c;那也会丢哦&#xff01; 改完那个分支&#xff0c;回到这个分支然后弹出来再。

Delphi 程序例子(DPI变化自动感知及显示器相关功能演示)

目录 一、前言 二、Delphi 演示程序&#xff08;D12版本&#xff0c;用D11也都可以&#xff09; 1. 演示程序功能&#xff1a; 2. 程序界面&#xff1a; 3. 程序源代码下载&#xff08;有偿&#xff09;&#xff1a; 一、前言 系列文章&#xff1a; 彻底搞懂 Windows 显示…

YOLOv5 | 卷积模块 | 提高网络的灵活性和表征能力的动态卷积【附代码+小白可上手】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 轻量级卷积神经网络由于其低计算预算限制了CNNs的深度&#xff08;卷积层数&#xff09;和宽度&#xff08;通道数&#xff09;&#xff0c;…

三分钟一条AI小和尚视频 ,日引300+创业粉。单日变现四位数 全套工具

经过六个月的不懈努力和无数次的尝试错误&#xff0c;我终于找到了一个高效引流和积累粉丝的新策略&#xff0c;并愿意与大家无私分享。这一次&#xff0c;我将详尽地介绍这个方法&#xff0c;建议朋友们多次观看以彻底掌握其精髓。 简而言之&#xff0c;该策略主要依托于AI绘…

C语言文件编程

C语言文件编程 第一部分 基本概念 1、Linux文件类型 1.-普通文件&#xff1a;存在于外部存储器中&#xff0c;用于存储普通数据。 1.txt 1.c 1.mp3 1.mp4 2.d目录文件&#xff1a;用于存放目录项&#xff0c;是文件系统管理的重要文件类型。 文件夹 3.p管道文件&#x…