[C++ 学习]变量和基本类型

今天是C++重新学习的第二天,因为是周末加上第一章内容比较少,两天就看完了前两章。

第二章的重点是指针引用const等,这些都是很令人头秃的,下面是第二章的学习笔记。

第二章 变量和基本类型

2.1 基本内置类型

C++定义了一套包括算术类型(arithmetic type)和空类型(void)在内的基本数据类型

2.1.1 算术类型

算术类型包括整型(包括字符型和布尔型)和浮点型

类型含义最小尺寸(bit)
bool布尔类型未定义
char字符8
wchar_t宽字符16
char16_tUnicode字符16
char32_tUnicode字符32
short短整型16
int整型16
long长整型32
long long长整型64
float单精度浮点数6为有效数字
double双精度浮点数10位有效数字
long double扩展精度浮点数10位有效数字

C++语言规定一个int至少和一个short一样大,一个long至少和一个int一样大,一个long long至少和一个long一样大。其中,数据类型long long是在C++11中新定义的

2.1.2 类型转换

程序应该尽量避免依赖于实现环境的行为。如果我们把int的尺寸看成是一个确定不变的已知值,那么这样的程序就称作不可移植的(nonportable)。当程序移植到别的机器上后,依赖于实现环境的程序就可能发送错误

当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数

2.1.3 字面值常量

一个形如42的值被称作字面值常量(literal),这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型

默认情况下,十进制字面值是带符号数,八进制和十六进制字面值既可能是带符号的也可能是无符号的。十进制字面值的类型是int、long、long long中尺寸最小的那个(例如,三者当中最小的是int),当然前提是这种类型要能容纳下当前值。八进制和十六进制字面值的类型是能容纳其数值的int、unsigned int、long、unsigned long 、long long和unsigned long long中的尺寸最小者。如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误。类型short没有对应的字面值。

尽管整型字面值可以存储在带符号数据类型中,但严格来说,十进制字面值不会是负数。如果我们使用了一个形如-42的负十进制字面值,那个符号并不在字面值之内,它的作用仅仅是对字面值取负值而已

指定字面值的类型

前缀含义类型
uUnicode 16字符char16_t
UUnicode 32字符char32_t
L宽字符wchar_t
u8UTF-8(仅用于字符串字面常量)char
后缀最小匹配类型
u or Uunsigned
l or Llong
ll or LLlong long
后缀类型
f or Ffloat
l or Llong double

2.2 变量

2.2.1 变量定义

在C++中,变量(variable)和对象(object)一般可以互换使用

对象是指一块能存储数据并具有某种类型的内存空间

列表初始化

int units_sold{0};
int units_sold(0);
// 当用内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器会报错

默认初始化

  • 定义于任何函数体之外的变量初始化为0
  • 定义在函数体内部的内置类型变量将不被初始化

2.2.2 变量声明和定义的关系

声明(declaration)使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。而定义(definition)负责创建于名字相关的实体

如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显示地初始化变量

C++是一种静态类型(statically typed)语言,其含义是在编译阶段检查类型。其中,检查类型的过程称为类型检查(type checking)

2.2.3 标识符

用户自定义的标识符不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。

2.2.4 作用域

可以使用::加变量名访问全局变量

2.3 复合类型

2.3.1 引用(reference)

一般在初始化对象时,初始值会被拷贝到新建的对象中,然而定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化

引用并非对象,相反的,它至是为一个已经存在的对象所起的另外一个名字

引用类型在初始化是必须与初始化对象类型相同,而引用在初始化后的赋值则不再与引用相关,只是简单的变量赋值(习题2.16)

2.3.2 指针(pointer)

指针与引用的不同

  • 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
  • 指针无须在定义时赋初值。和其它内置类型一样,在块作用域内定义的指针如果没有被初始化,它将拥有一个不确定的值。

在新标准下。现在的C++程序最好使用nullptr,同时尽量避免使用NULL

void是一种特殊的指针类型,可用于存放任意对象的地址,以void的视角来看内存空间也就仅仅是内存空间,没办法访问内存空间中所存的对象。

2.3.3 理解复合类型的声明

引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用

2.4 const限定符

  • 因为const对象一旦创建后其值就不能改变,所以const对象必须初始化(extern可以不用)。
  • 默认状态下,const对象仅在文件中有效
  • 编译器在编译过程中把用到该变量的地方都替换成对应的值。

externconst

某些时候有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。这种情况下,我们不希望编译器为每个文件分别生成独立的变量。相反,我们想让这类const对象像其它(非常量)对象一样工作,也就是说,只在一个文件中定义const,而在其他多个文件中声明并使用它。

解决的方法是,对于const变量不管声明还是定义都添加extern关键字,这样只需定义一次就可以了。

// file_1.cc定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize = fcn();
// file_1.h头文件
extern const int bufSize;	// 与file_1.cc中定义的是同一个

2.4.1 const的引用

在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量对象、字面值,甚至是一个一般表达式

允许将const int &绑定到一个普通int对象上,不允许将int&绑定到一个const int上

“一种例外”

double dval = 3.14;
const int &ri == dval;
//     ||
//   编译器转换
//     \/
double dval = 3.14;
const int temp = dval;
const int &ri = temp;

ri并没有绑定在dval上,而是绑定在temp上,temp只是一个临时变量,因为大家基本上不会想把引用绑定到临时量上,因此C++语言也就把这种行为归为非法

对const的引用可能引用一个并非const的对象

int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;		// 合法
r2 = 0;		// 非法

2.4.2 指针和const

pointer to const & const pointer

常量指针:不能改变其所指对象的值

指针常量:固定指向,无法更改指向的对象

2.4.3 顶层const

顶层const(top-level const) 表示指针本身是个常量,而用名词 底层const(low-level const) 表示指的对象是常量

更一般的,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。

当执行对象的拷贝操作时,拷入和拷出的对象必须有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行。

2.4.4 constexpr和常量表达式

**常量表达式(const expression)**是指值不会改变并且在编译过程就能得到计算结果的表达式。

const int max_files = 20;		// 是常量表达式
const int limit = max_files + 1;	// 是常量表达式
int staff_size = 27;			// 不是,因为可变
const int sz = get_size();		// 不是,因为编译过程无法直接计算结果

C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,且必须用常量表达式初始化。

constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size();		// 只有当size是一个constexpr函数时才是一条正确的声明语句

到现在为止接触的数据类型中,算术类型、引用和指针都属于字面值类型(literal type)

常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须是字面值类型

尽管指针和引用都能定义为constexpr,但它们的初始值却受到严格限制。一个constexpr指针的初始值必须是nullptr或者0,或者存储于某个固定地址中的对象。

函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。相反的,定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针。

指针和constexpr

在constexpr声明中定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。

const int *p = nullptr; 	// pointer to const
constexpr int *p = nullptr; // const pointer

2.5 处理类型

2.5.1 类型别名

// typedef
typedef double wages;		// wages是double的同义词
typedef wages base, *p; 	// base是double的同义词,p是double*的同义词

// using
using SI = Sales_item; 		// SI是sales_item的同义词

指针、常量和类型别名

typedef char *pstring;
const pstring cstr = 0; 	// cstr是指向char的常量指针
const pstring *ps;			// ps是一个指针,它的对象是指向char的常量指针

2.5.2 auto类型说明符

auto 让编译器通过初始值来推算变量的类型

使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个数据类型,所以该语句中所有变量的初始基本数据类型必须都一样。

auto 一般会忽略掉顶层const,同时底层const则会保留下来

2.5.3 decltype 类型指示符

decltype的作用是返回操作数的数据类型

decltype(f()) sum = x;		// sum的类型就是f()的返回类型

decltype和引用

如果表达式内容是解引用操作,则decltype将得到解引用类型。

int i = 42, *p = &i;
decltype(*p) c;		// 错误,c是int&,必须初始化

decltype((variable))的结果永远是引用,而decltype(variable)结果只要当variable本身就是一个引用时才是引用

2.6 其它

预处理器概述

预处理器变量无视C++语言中关于作用域的规则。

新手发博,恳请各位不吝赐教,不胜感激!

系列链接:
第一章 开始

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值