由endl对printf和cout的思考
【前言】二者的区别就不介绍了。二者使用方法:
- printf("%s",a);
- cout<<a<<endl;
endl的作用是什么?
一、endl作用
众所周知,endl有一个换行的作用,第二个作用就是清空缓冲区buffer。
为什么要清空缓冲区呢?
首先思考缓冲区存在的作用,缓冲区的作用一是为了避免频繁的I/O操作对磁盘的损耗,二是减少存取时的函数调用的损耗。所以,c++里面的缓冲区意义是非常大的,注意printf是没有缓冲区的。
我们上面即使没有加上endl,输出也会马上输出出来,那清空缓冲区的作用在哪呢?其实,我们这个程序太简单,不能体现。但是cout什么时候清空缓冲区我们是不确定的,这对于多线程来说增加了不可控,多线程只有栈区独享,所有线程共享原主进程的缓冲区,如果不及时清空缓冲区可能会造成串数据;cin更是从缓冲区读取数据,其中很多细节。
二、cout和printf时间复杂度比较
cout是对"<<”运算符的重载。其运行时间也慢于printf。有人做的测评:https://blog.csdn.net/eternity666/article/details/78001513
在vs下cout是对printf的封装,还要保持同步。但是在g++下不是,有人做的测试,g++的cout速度反而比printf快10倍左右。https://blog.csdn.net/qq_26398495/article/details/55105094
所以,在linux端编程,用cout就可以,没必要用printf.,而且更加明晰。
三、printf怎么实现“C多态”?
今天有个同学考我一个题目,很有意思。我们知道printf的参数列表,参数个数是不确定的,这就很类似C++里面的多态,那printf是怎么实现C“多态”的呢?
我第一次思考这个问题,就先写下当时怎么思考的吧?当然,他的问题是没有回答上来。
T1、C++里面的多态是怎么实现的?
C++是静态编译型语言,所谓的静态联编,但是使用虚函数即可实现动态联编,类似动态链接库。实现方法就是编译器提前生成vptr指针,运行的时候在通过指针找到虚函数表,找到函数入口地址。从指针的角度,好像和C多态有点关系,用函数指针?
顺着这个思路,我们知道,多态有编译期多态和运行期多态。编译期->重载,运行期->重写。
T2、重载重写(重定义?)三者的区别
T3、C++里面的运行期和编译期多态
(1)运行期多态(重写)
运行期多态的设计思想要归结到类继承体系的设计上去。对于有相关功能的对象集合,我们总希望能够抽象出它们共有的功能集合,在基类中将这些功能声明为虚接口(虚函数),然后由子类继承基类去重写这些虚接口,以实现子类特有的具体功能。典型地我们会举下面这个例子:
- class Animal
- {
- public :
- virtual void shout() = 0;
- };
- class Dog :public Animal
- {
- public:
- virtual void shout(){ cout << "汪汪!"<<endl; }
- };
- class Cat :public Animal
- {
- public:
- virtual void shout(){ cout << "喵喵~"<<endl; }
- };
- class Bird : public Animal
- {
- public:
- virtual void shout(){ cout << "叽喳!"<<endl; }
- };
- int main(){
- Animal * anim1 = new Dog;
- Animal * anim2 = new Cat;
- Animal * anim3 = new Bird;
- //藉由指针(或引用)调用的接口,在运行期确定指针(或引用)所指对象的真正类型,调用该类型对应的接口
- anim1->shout();
- anim2->shout();
- anim3->shout();
- //delete 对象
- ...
- return 0;
- }
运行期多态的实现依赖于虚函数机制。当某个类声明了虚函数时,编译器将为该类对象安插一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数地址。运行期间通过虚函数表指针与虚函数表去确定该类虚函数的真正实现。
总结:运行期多态通过虚函数发生于运行期。
(2)编译期多态(重载)
对模板参数而言,多态是通过模板具现化和函数重载解析实现的。以不同的模板参数具现化导致调用不同的函数,这就是所谓的编译期多态。相比较于运行期多态,实现编译期多态的类之间并不需要成为一个继承体系,它们之间可以没有什么关系,但约束是它们都有相同的隐式接口。我们将上面的例子改写为:
- class Animal
- {
- public :
- void shout() { cout << "发出动物的叫声" << endl; };
- };
- class Dog
- {
- public:
- void shout(){ cout << "汪汪!"<<endl; }
- };
- class Cat
- {
- public:
- void shout(){ cout << "喵喵~"<<endl; }
- };
- class Bird
- {
- public:
- void shout(){ cout << "叽喳!"<<endl; }
- };
- template <typename T>
- void animalShout(T & t){
- t.shout();
- }
- int main(){
- Animal anim;
- Dog dog;
- Cat cat;
- Bird bird;
- animalShout(anim);
- animalShout(dog);
- animalShout(cat);
- animalShout(bird);
- getchar();
- }
在编译之前,函数模板中t.shout()调用的是哪个接口并不确定。在编译期间,编译器推断出模板参数,因此确定调用的shout是哪个具体类型的接口。不同的推断结果调用不同的函数,这就是编译器多态。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用。
(3)二者的区别:超链接
T3、那么C实现“多态”是和C++ 类似的吗?
(1)重载实现(编译期)
看printf的源码:
- 1 #include <stdio.h>
- 2 #include <stdarg.h>
- 3
- 4
- 5 //va_start(arg,format),初始化参数指针arg,将函数参数format 右边第一个参数地址赋值给arg
- 6 //format必须是一个参数的指针,所以,此种类型函数至少要有一个普通的参数,
- 7 //从而提供给va_start ,这样va_start才能找到可变参数在栈上的位置。
- 8 //va_arg(arg,char),获得arg指向参数的值,同时使arg指向下一个参数,char用来指名当前参数型
- 9 //va_end 在有些实现中可能会把arg改成无效值,这里,是把arg指针指向了 NULL,避免出现野指针
- 10
- 11
- 12 void printf(const char *format, ...)
- 13 {
- 14 va_list arg;
- 15 va_start(arg, format);
- 16
- 17 while (*format)
- 18 {
- 19 char ret = *format;
- 20 if (ret == '%')
- 21 {
- 22 switch (*++format)
- 23 {
- 24 case 'c':
- 25 {
- 26 char ch = va_arg(arg, char);
- 27 putchar(ch);
- 28 break;
- 29 }
- 30 case 's':
- 31 {
- 32 char *pc = va_arg(arg, char *);
- 33 while (*pc)
- 34 {
- 35 putchar(*pc);
- 36 pc++;
- 37 }
- 38 break;
- 39 }
- 40 default:
- 41 break;
- 42 }
- 43 }
- 44 else
- 45 {
- 46 putchar(*format);
- 47 }
- 48 format++;
- 49 }
- 50 va_end(arg);
- 51 }
- 52 int main()
- 53 {
- 54 printf("%s %s %c%c%c%c%c!\n", "welcome", "to", 'C', 'h', 'i', 'n', 'a');
printf("%s %c","abc","c");//重载了使用
- 55 system("pause");
- 56 return 0;
- 57 }
void printf(const char *format,...);。注意到"..."这个可变参东西了吧,printf的“...”内部实际上是通过 __VA_ARGS__ 这个宏来实现的:链接。他有三个参数,具体可参考:这里
(2)重写(运行期)
这个就要通过函数指针了吧:https://blog.csdn.net/philip_puma/article/details/25973139
由endl对printf和cout的思考的更多相关文章
- 指向字符串的指针在printf与cout区别
根据指针用法: * 定义一个指针, &取变量地址, int b = 1; int *a = &b; 则*a =1,但对于字符串而言并非如此,直接打印指向字符串的指针打印的是地址还是字符 ...
- c++:printf和cout那个更好更快些
现在群里在讨论cout和printf那个快的问题,但我个人觉得printf好: 因为:printf对于一些数据大,以及保留小数位,字符……可以显示出明显的优势如“%s %d %c…………” 虽然pri ...
- c++ printf和cout的性能
今天做了一道编程题,仔细检查了算法并没有错误,但是结果显示时间超时,但仍有80%的案例通过了,很奇怪. 通过将cin换成scanf,cout换成printf结果AC,实验发现二者性能差了很多,在输出1 ...
- printf和cout的区别详述
https://blog.csdn.net/ysayk/article/details/50959909
- 8-cin cout PK scanf printf(速度快慢问题对比)
我们在c++ 中使用cin cout很方便但速度很慢,导致有些题目用cin就超时而用scanf则就ac了,那到底改用谁? cin慢是有原因的,其实默认的时候,cin与stdin总是保持同步的,也就是说 ...
- C语言中printf与i++,C++中的cout
一,printf与i++ 1,C语言中的printf是自右向左输出,. 2,而i++与++i不同的 i++首先取得i的值,下一行时候i = i + 1: ++i,首先i = i + 1,再取得i的值. ...
- C++ 学习笔记(一) cout 与printf 的不同之处
作为一个嵌入式开发的猿,使用打印调试程序是必不可少的,拿到新的项目第一件事就是捣鼓打印.这次也不例外有打印才有耍下去的底气.在之前零零碎碎的C++学习中,还是一边学一边做项目的状态下能用printf解 ...
- [笔记]cin、cout与scanf、printf的效率差异对比分析
之前上传UVa227 puzzle时,好不容易AC了,但发现自己用时50(ms),而在VJ上看到人家都是40ms.20ms,于是打开一个20ms的代码查看人家强在哪里.但结果研究了半天感觉差不多,于是 ...
- printf scanf cin cout的区别与特征
printf和scanf是c语言的输入输出,学习c++以后,自然是用cin cout这两个更简单的输入输出 printf scanf 都需要进行格式控制,比较麻烦,但优点是速度比较快,毕竟多做了一些事 ...
随机推荐
- Java数组模拟队列 + 优化
队列介绍 队列是一个有序列表,可以用数组或是链表来实现. 遵循先入先出的原则. 即:先存入队列的数据,要先取出.后存入的要后取出 示意图:(使用数组模拟队列示意图) 数组模拟队列 队列本身是有序列表 ...
- 十四自定义构建购物计算组件&表单组件
目录: 1.前言 2.组件介绍 3.js业务逻辑层 4.视图层 5.css属性设置 6.效果呈现 1.前言: 在第九篇文章购物车做好后,还忘记了一个至关重要的计算组件.在鸿蒙的组件中并没有提供这样一个 ...
- 翻译:《实用的Python编程》00_Setup
课程设置与概述 欢迎访问本课程(Practical Python Programming).这个页面包含一些关于课程设置的重要信息. 课程周期和时间要求 该课程最初是作为一个由讲师主导的,持续 3 - ...
- poj 2653 线段相交裸题(解题报告)
#include<stdio.h> #include<math.h> const double eps=1e-8; int n; int cmp(double x) { if( ...
- POJ2785 4 Values whose Sum is 0 (二分)
题意:给你四组长度为\(n\)序列,从每个序列中选一个数出来,使得四个数字之和等于\(0\),问由多少种组成情况(仅于元素的所在位置有关). 题解:\(n\)最大可以取4000,直接暴力肯定是不行的, ...
- HDU - 6287 口算训练 二分+质因数分解
小Q非常喜欢数学,但是他的口算能力非常弱.因此他找到了小T,给了小T一个长度为nn的正整数序列a1,a2,...,ana1,a2,...,an,要求小T抛出mm个问题以训练他的口算能力.每个问题给出三 ...
- 13. 从0学ARM-Cortex-A9 RTC裸机程序编写
一.RTC RTC(Real-Time Clock) 实时时钟. RTC是集成电路,通常称为时钟芯片.在一个嵌入式系统中,通常采用RTC来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关 ...
- EF Core数据访问入门
重要概念 Entity Framework (EF) Core 是轻量化.可扩展.开源和跨平台的数据访问技术,它还是一 种对象关系映射器 (ORM),它使 .NET 开发人员能够使用面向对象的思想处理 ...
- SpringSecurity简单使用
什么是SpringSecurity? Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置 ...
- 017.NET5_内置容器基本使用
IOC容器IServiceCollection .net 5已经内置了IOC容器. 什么是IOC? 把对象的创建交给第三方容器去创建 如何使用内置的 IOC IServiceCollection ? ...