第二章-变量和基本类型

基本内置类型

  • 赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数

举例:
8bit大小的unsigned char可以表示0 ~ 255一共256个数
把-1赋给unsigned char会得到255
运算方式为:
(-1) % 256
= (-1 + 256) % 256
= 255 % 256
= 255

  • 给带符号类型超出它表示范围的值时,结果是未定义的
  • 当一个算术表达式中既有无符号数又有int时,那个int会转换为无符号数
unsigned u = 10;
int i = -42;
cout << u + i << endl;
//将-42转换为无符号数,结果等于负数加上无符号数的模
//无符号数的模即无符号数的计量范围,如果机器int占32位,则是2的32次方
//2的32次方+(-42) = 4294967264
  • 注意不要有这样的想法:反正不打算输出负数,就用无符号数来写循环
for (unsigned u = 10; u >= 0; --u)
	cout << u << endl;
//当u变成0时,继续执行--u变为-1
//不满足无符号数要求,如果int占32位,将会转换为2的32次方+(-1)
//即死循环
  • 形如42的值被称作字面值常量。严格来说十进制字面值不会是负数,-42这样的字面值,负号并不在字面值内,它的作用仅仅是对字面值取负值

变量

  • 初始化和赋值是两个概念。初始化是创建变量时赋予其一个初始值,赋值是把对象当前的值擦除再用一个新的值替代
  • 声明使得名字为程序所知,而定义负责创建与名字关联的实体。区别就是声明仅用来告诉编译器变量的名称和类型,而不分配内存。
  • 如果想声明一个变量并且不定义它,就在变量名前加上extern,而且不能显式地初始化变量。声明extern表示i在别的文件中已经定义,提示编译器遇到此变量时在其它模块中寻找其定义。
extern int i; //声明i且没有定义
int j; //声明且定义
//extern语句包含初始值就不再是声明,而是定义了,如extern int i = 10;
  • 变量只能定义一次,但能声明多次
  • 新建局部变量能覆盖同名全局变量

复合类型

  • 引用一旦初始化完成就和初始值对象一直绑定在一起。不能令引用重新绑定到另外一个对象,也因此引用必须被初始化
  • 引用本身不是对象,所以不能定义引用的引用
  • 引用只能绑定在对象上,不能与字面值或表达式的计算结果绑定
int &ref = 10; //出错
  • 引用本身不是对象,所以不能定义指向引用的指针
  • 两个指针存放的地址值相同,则它们相等
  • void*是一种特殊的指针类型,可用于存放任意对象的地址。但是不能直接操作void/*指针所指对象
double obj = 3.14, *pd = &obj;
void *pv = &obj;
pv = pd;
  • 指针是对象,所以存在对指针的引用
int *p;
int *&r = p; //r是对指针p的引用
//从右往左读,r首先是引用,*表示引用的类型是int *

const限定符

  • const对象必须初始化
  • const对象被设定为仅在文件内有效。当多个文件出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量
  • 如果想在一个文件中定义const,而在其他多个文件中声明并使用它,可以对const变量(不管声明还是定义)都加上extern。
//文件1
extern const int bufsize = fcn();
//文件2
extern const int bufsize; //和文件1是同一个
  • 引用类型必须与其所引用对象的类型一致,但是有两个例外。一是可以在初始化常量引用时允许用任意表达式作为初始值
int i = 42;
const int &r1 = i; //√ 可以将const int绑定到普通int对象上
const int &r2 = 42; //√
const int &r3 = r1 * 2; //√
int &r4 = r1 * 2 //× r4是普通的int,不能引用const
  • 二是允许令一个指向常量的指针指向一个非常量对象
const double pi = 3.14;
double *ptr = &pi; //错,ptr是普通指针
const double *cptr = &pi;
double dval = 3.14;
cptr = &dval; //√
  • 顶层const:本身是const;底层const:指向的是const
  • int *const ptr 从右向左看,ptr先是const,说明不能修改自己,即不能修改指向的地址,但是可以修改地址内的值。因为自己是常量,所以是顶层const
  • const int *ptr 从右向左看,ptr是int指针,指向的是const int,说明不能修改指向的值,但是可以修改地址。因为指向的是常量,所以是底层const
  • 执行拷贝操作时,拷入拷出对象必须有相同的底层const资格,或者两个对象的数据类型必须能够转换
int i = 0;
int *const p1 = &i; //顶层
const int ci = 42; //顶层
const int *p2 = &ci; //底层
const int *const p3 = p2; //左边底层const,右边顶层const
const int &r = ci; //声明引用的const都是底层const
int *p = p3; //错误,p3包含底层const定义,p没有
p2 = p3; //p2 p3都是底层const
p2 = &i; //允许一个指向常量的指针指向非常量对象
int &r = ci //错误,普通int&不能绑定到int常量上
const int &r2 = i; //const int&可以绑定到普通int上

constexpr和常量表达式

  • 常量表达式:1.值不会改变 2.编译过程就能得到计算结果的表达式
const int max_files = 20; //常量表达式
const int limit = max_files + 1; //常量表达式
int staff_size = 27; //非常量表达式,因为是普通int
const int sz = get_size(); //get_size的值要运行时才知道,违反第二条,所以不是常量表达式
  • 声明constexpr的变量一定是常量,而且必须用常量表达式初始化
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size(); //size()是constexpr函数时才是正确的语句
  • constexpr指针的初始值必须是nullptr或0,或者是存储于某个固定地址中的对象
  • 函数体内定义的变量一般来说不存放在固定地址中,所以constexpr指针不能指向这样的变量。而定义于函数体外的对象地址固定不变,能用来初始化constexpr指针
  • 在constexpr声明中如果定义一个指针,限定符constexpr仅对指针有效,和指针所指对象无关。换句话说constexpr把它定义的对象置为顶层const
const int *p = nullptr; //指向常量的指针
constexpr int *q = nullptr; //指向整数的常量指针
  • 因为常量指针,所以constexpr指针既能指向常量,也能指向非常量
constexpr int *np = nullptr; //np是指向整数的常量指针,值为空
int j = 0;
constexpr int i = 42; //i的类型是整型常量
//i和j必须定义在函数体外
constexpr const int *p = &i; //p是常量指针,指向整型常量i
constexpr int *p1 = &j; //p1是常量指针,指向整数j

处理类型

  • typedef \ using定义类型的别名
using SI = Sales_item;
typedef double wages;
typedef wages base, *p; //base是double同义词,p是double*同义词
//上句可以分开成两句
//typedef wages base;
//typedef wages *p;
//理解技巧:将typedef遮起来,可以看出就像在定义变量
//double wages; double base; double *p
//然后变量名就可以当作自己的类型的别名了
  • 用auto能让编译器来分析表达式所属类型。auto必须有初始值\
  • 因为一条语句只能由一个基本数据类型,所以语句中所有变量的初始基本数据类型必须一致
auto sz = 0, pi = 3.14;
//错误!sz和pi的类型不一致
  • auto一般忽略顶层const而保留底层const
int i = 0;
const int ci = i, &cr = ci;
auto b = ci; //b是int,忽略了ci的顶层const
auto c = cr; //cr是int
auto d = &i; //d是int*
auto e = &ci; //e是const int*,对常量对象取地址是一种底层const
const auto f = ci; //明确指明f要是const int
  • 设置类型为auto的引用时,初始值中的顶层const属性会保留
auto &g = ci; //g是const int&
auto &h = 42; //错误,不能为非常量引用绑定字面值
const auto &j = 42; //正确
  • 切记符号&和*属于声明符,而不是基本数据类型的一部分
auto &m = ci, *p = &ci;
auto &n = i, *p2 = &ci; //错误
//n是int,p2是const int,矛盾了
  • decltype返回操作数的数据类型
decltype(f()) sum = x; //sum类型就是f()返回类型
  • 如果decltype使用的表达式是变量,则返回该变量的类型(包括顶层const和引用)
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x是const int
decltype(cj) y = x; //y是const int&
decltype(cj) z; //出错,z是const int&但是没有初始化
  • 如果使用的表达式不是变量,则返回表达式结果对应的类型
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; //正确,加法的结果是int
decltype(*p) c; //错误,c是int&,必须初始化
  • 如果给变量加上一层或多层括号,编译器会把它当作表达式。变量是可以作为赋值语句左值的特殊表达式,所以decltype会得到引用类型
decltype((i)) d; //错误,d是int&,要初始化
//decltype((variable))的结果永远是引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值