【前言】二者的区别就不介绍了。二者使用方法:

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、重载重写(重定义?)三者的区别 
       (1)成员函数重载特征:
      相同的范围(在同一个类中);
      函数名字相同
      参数不同;
      virtual关键字可有可无;
  (2) 重写(覆盖)是指派生类函数覆盖基类virtual函数,特征是:
      不同的范围,分别位于基类和派生类中;
      函数的名字相同;
      参数相同;
      基类函数必须有virtual关键字;
  (3)重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
      如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual,基类的函数被隐藏;
      如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有vitual关键字,此时,基类的函数被隐藏。
  (条款36:不应该重定义任何非虚函数,既然需要你重定义,那为什么不直接在基类定义虚函数?非虚函数就是表明此函数具有“不变性凌驾特异性”,所以尽量不要重定义)

  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的思考的更多相关文章

  1. 指向字符串的指针在printf与cout区别

    根据指针用法: * 定义一个指针, &取变量地址, int b = 1; int *a = &b; 则*a =1,但对于字符串而言并非如此,直接打印指向字符串的指针打印的是地址还是字符 ...

  2. c++:printf和cout那个更好更快些

    现在群里在讨论cout和printf那个快的问题,但我个人觉得printf好: 因为:printf对于一些数据大,以及保留小数位,字符……可以显示出明显的优势如“%s %d %c…………” 虽然pri ...

  3. c++ printf和cout的性能

    今天做了一道编程题,仔细检查了算法并没有错误,但是结果显示时间超时,但仍有80%的案例通过了,很奇怪. 通过将cin换成scanf,cout换成printf结果AC,实验发现二者性能差了很多,在输出1 ...

  4. printf和cout的区别详述

    https://blog.csdn.net/ysayk/article/details/50959909

  5. 8-cin cout PK scanf printf(速度快慢问题对比)

    我们在c++ 中使用cin cout很方便但速度很慢,导致有些题目用cin就超时而用scanf则就ac了,那到底改用谁? cin慢是有原因的,其实默认的时候,cin与stdin总是保持同步的,也就是说 ...

  6. C语言中printf与i++,C++中的cout

    一,printf与i++ 1,C语言中的printf是自右向左输出,. 2,而i++与++i不同的 i++首先取得i的值,下一行时候i = i + 1: ++i,首先i = i + 1,再取得i的值. ...

  7. C++ 学习笔记(一) cout 与printf 的不同之处

    作为一个嵌入式开发的猿,使用打印调试程序是必不可少的,拿到新的项目第一件事就是捣鼓打印.这次也不例外有打印才有耍下去的底气.在之前零零碎碎的C++学习中,还是一边学一边做项目的状态下能用printf解 ...

  8. [笔记]cin、cout与scanf、printf的效率差异对比分析

    之前上传UVa227 puzzle时,好不容易AC了,但发现自己用时50(ms),而在VJ上看到人家都是40ms.20ms,于是打开一个20ms的代码查看人家强在哪里.但结果研究了半天感觉差不多,于是 ...

  9. printf scanf cin cout的区别与特征

    printf和scanf是c语言的输入输出,学习c++以后,自然是用cin cout这两个更简单的输入输出 printf scanf 都需要进行格式控制,比较麻烦,但优点是速度比较快,毕竟多做了一些事 ...

随机推荐

  1. Codeforces Round #628 (Div. 2) D. Ehab the Xorcist(异或,思维题)

    题意: 寻找异或后值为 u,相加后和为 v 的最短数组. 思路: 异或得 u ,则 v 至少应大于等于 u ,且多出来的部分可以等分为两份相消. 即初始数组为 u , (v-u)/2 , (v-u)/ ...

  2. zoj2112 Dynamic Rankings (主席树 || 树套树)

    The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with t ...

  3. 【noi 2.6_1808】最长公共子序列(DP)

    题意:给2个字符串求其最大公共子序列的长度.解法:这个和一般的状态定义有点不一样,f[i][j]表示 str 前i位和 str2 前j的最大公共子序列的长度,而不是选 str 的第i位和 str2 的 ...

  4. Codeforces Round #681 (Div. 1, based on VK Cup 2019-2020 - Final) B. Identify the Operations (模拟,双向链表)

    题意:给你一组不重复的序列\(a\),每次可以选择一个数删除它左边或右边的一个数,并将选择的数append到数组\(b\)中,现在给你数组\(b\),问有多少种方案数得到\(b\). 题解:我们可以记 ...

  5. IntelliJ IDEA 运行java程序时出现“程序发生找不到或无法加载主类 cn.test1.test1”错误

    在你程序不出现错误,而且你的编译器已经成功导入后 成功导入的样子 你可以重新打开一个项目 这就可以了^_^

  6. 加密算法——RSA算法(c++简单实现)

    RSA算法原理转自:https://www.cnblogs.com/idreamo/p/9411265.html C++代码实现部分为本文新加 RSA算法简介 RSA是最流行的非对称加密算法之一.也被 ...

  7. MySQL 事务日志

    重做日志(Redo log) 重做日志(Redo log),也叫做前滚日志,存放在如下位置,轮询使用,记录着内存中数据页的变化,在事务 ACID 过程中,主要实现的是 D(Durability)的作用 ...

  8. K8S(05)核心插件-ingress(服务暴露)控制器-traefik

    K8S核心插件-ingress(服务暴露)控制器-traefik 1 K8S两种服务暴露方法 前面通过coredns在k8s集群内部做了serviceNAME和serviceIP之间的自动映射,使得不 ...

  9. K8S(01)二进制部署实践-1.15.5

    系列文章说明 本系列文章,可以基本算是 老男孩2019年王硕的K8S周末班课程 笔记,根据视频来看本笔记最好,否则有些地方会看不明白 需要视频可以联系我 目录 系列文章说明 1 部署架构 1.1 架构 ...

  10. hihocoder 1631

    时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU campus. They are all happy ...