[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
类型的可能取值范围
#include <limits>
std::numeric_limits<类型>::max() 或 min() 求最大值,最小值
unsigned 能表示更大的数值范围
unsigned 在移位操作时很重要
类型的别名
typedef 可以给类型赋予别名,与原名等价
书写更简便,尤其时复合类型时
例如:typedef long long int LL;
变量及其作用域
变量好比一个小盒子,能容纳数据,有名字,有地址,有大小,类型。。。
两个重要概念:
生存期:什么时候分配内存,什么时候释放内存
作用域:在什么位置可见,什么位置不可见
全局变量
定义在函数外的变量,静态的生存期
全局变量可以初始化,可以用表达式 全局变量可以在多个函数间,多个文件间共享 全局位置不能执行非定义语句
int a = 100;
int b = 200;
int c;
c = a + b; // 编译错误!
int main()
{
cout << c << endl;
return 0;
}
此是反面教材, 不能在全局位置执行其它语句
变量可以在声明时初始化,也可以只声明。 当未初始化时,变量的值是随机的,此是很多bug发源地
局部变量:
函数内部定义,函数执行时存在
局部变量可以和全局同名,会产生覆盖效果
常数与常量
先看看最粗略的内存结构。
image-20211205212212657
常数
也叫立即数,或者“字面量”,就是硬编码到程序中的那些“死”数据。
字符常数 单引号:’A’, ‘x’ 有些字符无法表示,可以直接整数,或:转义字符
char a = 'A';
char b = 9;
char c = 66;
‘\t’ 与 9 是同等效果的 ‘\n’ 是什么?它的 ASCII 码是多少? 可以实验确定
char a = '\n';
int x = a;
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 修饰的变量,它占用数据区内存,但编译器保证你不能修改它的值。
常量必须在定义时初始化
编译器的保证只是形式上的,你可以绕开编译器,强行改变其值! 常量的用处:
多个地方用到同一个常量,内存只有一份拷贝。 程序中不希望修改的量,避免“笔误” 程序中不希望出现魔法数字,这样不利于将来的变更
枚举类型
把一组常数符号化,比如:星期,职称等有限种类的情形
enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun} a; 实际上相当于:
enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun};
enum week a;
这实际上是同时定义了三样东西: 类型: enum week 类型中的常数符号: Wed, Sat 等 该类型的变量: a
enum 的典型错误用法:
enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun};
int a = Thu;
//a = 100;
cout << a << endl;
可以通多 typedef 来定义enum的别名,避免定义多个变量时的尴尬。
typedef enum {Mon, Tue, Wed, Thu, Fri, Sat, Sun} WEEK;
类型修饰符
对大类型进行细化, short long signed unsigned
const 常量,
volatile 不要进行自动优化
存储修饰符
auto 默认的类型,称为:自动变量、栈变量、局部变量 c++17 已经把 auto 挪为他用,不再是类型修饰符
register 直接放寄存器中,速度快。c++17 开始废弃
static 修饰,静态空间,与全局变量同在
extern 跨文件的伪声明
并不分配内存,只是告诉编译器,全局变量会在另一个地方声明
内存的类型
程序分配的内存,主要在“栈”和“堆”这两部分。“堆”比“栈”复杂
只读代码: 存代码,也包括立即数,以及定常资源
静态空间: 存放static变量,全局变量,常量
栈区(stack): 自动变量,函数执行时的上下文环境
堆(heap): 程序运行中,动态地申请及归还
堆和栈的比较
比较项目 栈 堆 说明
谁来管理 自动申请,自动释放 程序员代码申请,代码释放
时间特性 后申请的,必然先释放 没有限制
空间特性 所有申请的空间都是连续在一起的 空间上没有规律 可能产生能“碎片”
大小限制 有大小限制,默认2M 大小理论上没有限制
响应速度 很快 较栈慢
常见错误 栈溢出 悬挂指针,内存泄漏 栈的大小可设置
优美的栈
后进先出的结构(LIFO)
对比:heap 是什么结构? 自由进出
栈能实现沿着原路返回的功能
想一想: 很多人爬山,山脚下堆放衣服,回来拿衣服的时,怎样不会纠缠在一起??想一想: 很多人爬山,山脚下堆放衣服,回来拿衣服的时,怎样不会纠缠在一起??
特点是:自动分配,自动释放
缺点: 栈溢出 stackoverflow
选择与分支选择与分支
根据条件的真假,决定执行哪路代码 if .. else.. switch ?: 都有这个能力
if 的条件中尽量用 bool, 其它类型会强制转为bool
if 的条件中可能会发生短路求值现象”if(a表达式 && b表达式) …” 当 a表达式为假的时候,b表达式根本不会执行 同理,”if(a表达式 || b表达式) …”
int a = 5, b = 8;
int x = 100;
if(a>10 && (x=b)){
cout << "here" << endl;
}
cout << x << endl;
单 if 语句
尽量使用这个语句,避免 else
试一试: 给定年份,求是不是闰年
bool leap_year = x % 4 == 0 && x % 100 != 0 || x % 400 == 0;
晕不晕? 可以采用渐进的方案,逐渐接近目标:假设修正法
if .. else
if 语句是可以嵌套的,嵌套是 bug 的发源地之一(哪个else 对应哪个if)
试一试:给定 a, b, c 三个数,求居中的一个(假设没有相等的情况)
int a = 5, b = 2, c = 8;
if(a>b){
if(b>c)
cout << b << endl;
else
if(c>a) cout << a << endl;
else cout << c << endl;
}
else{
if(b<c)
cout << b << endl;
else
if(c<a) cout << a << endl;
else cout << c << endl;
}
有很多的改进方法:
使用函数,避免 if 嵌套
使用 ?: 表达式
使用”冒泡排序法”
// 伪代码
if(a>b) a <---> b
if(b>c) b <---> c
if(a>b) a <---> b
print(b)
?: 表达式
?: 是唯一的三目运算符?: 是唯一的三目运算符
xxx = a ? b : c; //重点在求值,不在分支xxx = a ? b : c; //重点在求值,不在分支
与 if else 相比,它的重点是求值,而不是:执行一系列动作 改进的求居中方案:
int a = 5, b = 2, c = 8;
if(a>b && a>c) cout << (b > c? b : c) << endl;
if(b>a && b>c) cout << (a > c? a : c) << endl;
if(c>a && c>b) cout << (a > b? a : b) << endl;
试一试: 给定 a, b, c 三个数,求最大值 不要想复杂了,这可以用”擂台赛”的方式解决
提示: 先让 a 上台,然后挑战者与之比试,胜者留在擂台,继续接受挑战。。。