[TOC]This is a book

基本数据类型

c++的数据类型分为:

基本数据类型:布尔,整数,浮点

派生类型:修饰类型,指针类型,用户定义的复合类型等

基本类型表

关键字 含义 说明

bool 布尔 表示是否的概念 false true 0 1

int 整数 表示计数或数量

float 单精度 近似表达实数的概念

double 双精度 近似表达实数

char 字符 代表一个ASCII码符号 -128 - 127

void 无 表示没有,或者空的概念

基本类型占用空间

ps. 一般IDE注释和取消注释的方案:行选中,ctrl + /

sizeof(变量) 或者 sizeof(类型) 来探测

sizeof 是关键字,不是函数

不同的平台,不同的硬件,不同的编译环境可以有差异,不要武断!

类型修饰符

==short== 更短

==long== 更长

==signed== 有符号

==unsigned== 无符号

如果省略了基本类型,默认为 int

short = short int

unsigned = unsigned int

类型能多次修饰

long long = long long int

类型的可能取值范围

  1. #include <limits>

std::numeric_limits<类型>::max() 或 min() 求最大值,最小值

unsigned 能表示更大的数值范围

unsigned 在移位操作时很重要

类型的别名

typedef 可以给类型赋予别名,与原名等价

书写更简便,尤其时复合类型时

例如:typedef long long int LL;

变量及其作用域

变量好比一个小盒子,能容纳数据,有名字,有地址,有大小,类型。。。 a book - 图1

两个重要概念:

生存期:什么时候分配内存,什么时候释放内存

作用域:在什么位置可见,什么位置不可见

全局变量

定义在函数外的变量,静态的生存期

全局变量可以初始化,可以用表达式 全局变量可以在多个函数间,多个文件间共享 全局位置不能执行非定义语句

  1. int a = 100;
  2. int b = 200;
  3. int c;
  4. c = a + b; // 编译错误!
  5. int main()
  6. {
  7. cout << c << endl;
  8. return 0;
  9. }

此是反面教材, 不能在全局位置执行其它语句

变量可以在声明时初始化,也可以只声明。 当未初始化时,变量的值是随机的,此是很多bug发源地

局部变量:

函数内部定义,函数执行时存在

局部变量可以和全局同名,会产生覆盖效果

常数与常量

先看看最粗略的内存结构。

image-20211205212212657

常数

也叫立即数,或者“字面量”,就是硬编码到程序中的那些“死”数据。

字符常数 单引号:’A’, ‘x’ 有些字符无法表示,可以直接整数,或:转义字符

  1. char a = 'A';
  2. char b = 9;
  3. char c = 66;

‘\t’ 与 9 是同等效果的 ‘\n’ 是什么?它的 ASCII 码是多少? 可以实验确定

  1. char a = '\n';
  2. int x = a;
  3. cout << x << endl;

整型常数 默认是 int,需要长整数需要加 LL

考考你,一年有多少秒? int a = 365 24 60 60 1000; ???

十六进制表示法: 0xff, 0xBA3C

二进制与十六进制有天然对应关系:

cout << hex << x<< endl;

浮点型常数 可以使用 f 或 d 尾标表示类型 可以使用科学计数法表示更大的数字

0.5e12, 3.18e-20

常量

用 const 修饰的变量,它占用数据区内存,但编译器保证你不能修改它的值。

常量必须在定义时初始化

编译器的保证只是形式上的,你可以绕开编译器,强行改变其值! 常量的用处:

多个地方用到同一个常量,内存只有一份拷贝。 程序中不希望修改的量,避免“笔误” 程序中不希望出现魔法数字,这样不利于将来的变更

枚举类型

把一组常数符号化,比如:星期,职称等有限种类的情形

  1. enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun} a; 实际上相当于:
  2. enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun};
  3. enum week a;

这实际上是同时定义了三样东西: 类型: enum week 类型中的常数符号: Wed, Sat 等 该类型的变量: a

enum 的典型错误用法:

  1. enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun};
  2. int a = Thu;
  3. //a = 100;
  4. cout << a << endl;

可以通多 typedef 来定义enum的别名,避免定义多个变量时的尴尬。

  1. typedef enum {Mon, Tue, Wed, Thu, Fri, Sat, Sun} WEEK;

类型修饰符

对大类型进行细化, short long signed unsigned

const 常量,

volatile 不要进行自动优化

存储修饰符

auto 默认的类型,称为:自动变量、栈变量、局部变量 c++17 已经把 auto 挪为他用,不再是类型修饰符

register 直接放寄存器中,速度快。c++17 开始废弃

a book - 图2

static 修饰,静态空间,与全局变量同在

extern 跨文件的伪声明

并不分配内存,只是告诉编译器,全局变量会在另一个地方声明

内存的类型

程序分配的内存,主要在“栈”和“堆”这两部分。“堆”比“栈”复杂

a book - 图3

只读代码: 存代码,也包括立即数,以及定常资源

静态空间: 存放static变量,全局变量,常量

栈区(stack): 自动变量,函数执行时的上下文环境

堆(heap): 程序运行中,动态地申请及归还

a book - 图4

堆和栈的比较

比较项目 栈 堆 说明

谁来管理 自动申请,自动释放 程序员代码申请,代码释放
时间特性 后申请的,必然先释放 没有限制
空间特性 所有申请的空间都是连续在一起的 空间上没有规律 可能产生能“碎片” 大小限制 有大小限制,默认2M 大小理论上没有限制
响应速度 很快 较栈慢
常见错误 栈溢出 悬挂指针,内存泄漏 栈的大小可设置

优美的栈

后进先出的结构(LIFO)

对比:heap 是什么结构? 自由进出

栈能实现沿着原路返回的功能

想一想: 很多人爬山,山脚下堆放衣服,回来拿衣服的时,怎样不会纠缠在一起??想一想: 很多人爬山,山脚下堆放衣服,回来拿衣服的时,怎样不会纠缠在一起??

特点是:自动分配,自动释放

缺点: 栈溢出 stackoverflow

选择与分支选择与分支

根据条件的真假,决定执行哪路代码 if .. else.. switch ?: 都有这个能力

if 的条件中尽量用 bool, 其它类型会强制转为bool

a book - 图5

if 的条件中可能会发生短路求值现象”if(a表达式 && b表达式) …” 当 a表达式为假的时候,b表达式根本不会执行 同理,”if(a表达式 || b表达式) …”

  1. int a = 5, b = 8;
  2. int x = 100;
  3. if(a>10 && (x=b)){
  4. cout << "here" << endl;
  5. }
  6. cout << x << endl;

单 if 语句

尽量使用这个语句,避免 else

试一试: 给定年份,求是不是闰年

bool leap_year = x % 4 == 0 && x % 100 != 0 || x % 400 == 0;

晕不晕? 可以采用渐进的方案,逐渐接近目标:假设修正法

a book - 图6

if .. else

if 语句是可以嵌套的,嵌套是 bug 的发源地之一(哪个else 对应哪个if)

试一试:给定 a, b, c 三个数,求居中的一个(假设没有相等的情况)

  1. int a = 5, b = 2, c = 8;
  2. if(a>b){
  3. if(b>c)
  4. cout << b << endl;
  5. else
  6. if(c>a) cout << a << endl;
  7. else cout << c << endl;
  8. }
  9. else{
  10. if(b<c)
  11. cout << b << endl;
  12. else
  13. if(c<a) cout << a << endl;
  14. else cout << c << endl;
  15. }

有很多的改进方法:

使用函数,避免 if 嵌套

使用 ?: 表达式

使用”冒泡排序法”

  1. // 伪代码
  2. if(a>b) a <---> b
  3. if(b>c) b <---> c
  4. if(a>b) a <---> b
  5. print(b)

?: 表达式

?: 是唯一的三目运算符?: 是唯一的三目运算符

xxx = a ? b : c; //重点在求值,不在分支xxx = a ? b : c; //重点在求值,不在分支

与 if else 相比,它的重点是求值,而不是:执行一系列动作 改进的求居中方案:

  1. int a = 5, b = 2, c = 8;
  2. if(a>b && a>c) cout << (b > c? b : c) << endl;
  3. if(b>a && b>c) cout << (a > c? a : c) << endl;
  4. if(c>a && c>b) cout << (a > b? a : b) << endl;

试一试: 给定 a, b, c 三个数,求最大值 不要想复杂了,这可以用”擂台赛”的方式解决

提示: 先让 a 上台,然后挑战者与之比试,胜者留在擂台,继续接受挑战。。。