作者简介: 博 主 在 读 机 器 人 研 究 生 , 目 前 研 一 。 对 计 算 机 后 端 感 兴 趣 , 喜 欢 c + + , g o , p y t h o n , 目 前 熟 悉 c + + , g o 语 言 , 数 据 库 , 网 络 编 程 , 了 解 分 布 式 等 相 关 内 容 \textcolor{orange}{博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++,go语言,数据库,网络编程,了解分布式等相关内容} 博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++,go语言,数据库,网络编程,了解分布式等相关内容
个 人 主 页 : \textcolor{gray}{个人主页:} 个人主页: 小呆鸟_coding
支 持 : \textcolor{gray}{支持:} 支持: 如 果 觉 得 博 主 的 文 章 还 不 错 或 者 您 用 得 到 的 话 , 可 以 免 费 的 关 注 一 下 博 主 , 如 果 三 连 收 藏 支 持 就 更 好 啦 \textcolor{green}{如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦} 如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦 就 是 给 予 我 最 大 的 支 持 ! \textcolor{green}{就是给予我最大的支持!} 就是给予我最大的支持!
本文摘要
本专栏主要是对c++ primer这本圣经的总结,以及每章的相关笔记。目前正在复习这本书。同时希望能够帮助大家一起,学完这本书。 本文主要讲解第2章 变量和基本类型 文章目录
- 第2章 变量和基本类型
- 2.1 基本内置类型
- 2.1.1 算术类型
- 2.1.2 类型转换
- 2.1.3 字面值常量
- 2.2 变量
- 2.2.1 变量的定义
- 2.2.2 变量声明和定义
- 2.2.3 标识符
- 2.2.4 名字作用域
- 2.3 复合类型
- 2.3.1 引用
- 2.3.2 指针
- 2.3.3 理解复合类型的声明
- 2.4 const限定符
- 2.4.1 const的引用
- 2.4.2 指针和const
- 2.4.3 顶层const
- 2.4.4 constexpr和常量表达式
- 2.5 处理类型
- 2.5.1 类型别名
- 2.5.2 auto类型说明符
- 2.5.3 decltype类型指示符
- 2.6 自定义数据结构
- 2.6.1 定义Sales_data类型
- 2.6.2 使用Sales_data类(没东西,暂时省略hhh)
- 2.6.3 编写自己的头文件
概述 c ++定义了几种基本的内置类型(如字符、整型、浮点数等),同时提供自动以数据类型的机制。
基本内置类型包括算数类型和空类型。算数类型包括字符、整型数、浮点数和布尔值。
类型 | 含义 | 最小尺寸 |
---|---|---|
bool |
布尔类型 | 8bits |
char |
字符 | 8bits |
wchar_t |
宽字符 | 16bits |
char16_t |
Unicode字符 | 16bits |
char32_t |
Unicode字符 | 32bits |
short |
短整型 | 16bits |
int |
整型 | 16bits (在32位机器中是32bits) |
long |
长整型 | 32bits |
long long |
长整型 | 64bits (是在C++11中新定义的) |
float |
单精度浮点数 | 6位有效数字 |
double |
双精度浮点数 | 10位有效数字 |
long double |
扩展精度浮点数 | 10位有效数字 |
类型选择
int 类型
,如果大的话选用long long
类型,一般short与 long不用char
和bool
.因为char类型在一些机器上有符号,而到了另外机械就无符号。如果需要使用char,就指明是signed char
或者unsigned char
double
,因为float精度可能不够。long double没必要,消耗内存。概述几种类型转换:
仅保留小数点前面的部分。
结果是初始值对无符号类型表示数值总数取模后的余数
,例如-1 赋给 8 位 unsigned char 的结果是 255。未定义
的,程序可能会崩掉取模与取余区别:
取余运算在取c值时,向0方向舍入,而取模运算在取c时,向负无穷方向舍入
// 举例 那题目当中的-1 举例
-1 MOD 256
第一步求商:
c = [a / b] = -1 / 256 = -1 //结果为-0.0039,向负无穷取整为1
第二步求模:
r = a - c * b = (-1) - (-1) * 256 = 255
//或者这样理解
计算机在存储-1时是存储的-1的补码,对于8位unsigned char来说-1的原码为1000 0001,它的反码为(符号位不变,其他位置取反)1111 1110,则补码为(补码= 反码+1),1111 1111,换成十进制整数为255
含有无符号的整数
总结:无符号参与运算时,结果肯定不是负数,如果是负数则需要对结果取模
整型和浮点型字面值
整型字面值中 0 开头的整数是 8 进制,0x 开头的整数是十六进制。
浮点型字面值可以用小数或科学计数法表示,科学计数法中的指数部分用 E 或 e 标识。
3.1415926 0. 0e0 .001 3.14159E2
字符和字符串字面值
'c' 字符字面值
"Hell world" 字符串字面值比实际值多1,因为它的最后多一个'\0'(空字符)
hello world被看做一个整体,只要字符串字面值位置仅有空格、缩进、换行符分隔,就看做一个整体
转义序列
换行符:\n | 横向制表符:\t | 报警符:\a |
---|---|---|
纵向制表符:\v | 退格符:\b | 双引号:\" |
反斜线:\\ | 单引号:\’ | 问号:? |
回车符:\r | 进纸符:\f |
布尔字面值和指针字面值
true false 是布尔类型的字面值
nullptr 是指针字面值
变量 :提供一个具名的、可供程序操作的存储空间
对象 :一块能存储数据并具有某种类型的内存
对于c++,而言变量和对象可以互换使用
初始化
对象在创建的过程中就获得了一个值,此时这个对象被初始化。
初始化和赋值的区别:
列表初始化
用一对{ }来表示列表初始化
int i = 0;
int i = {0};
int i {0};
int i (0);
特点
:如果使用列表初始化,且初始值存在,丢失情况,则编译器会报错
long double ld = 3.141592;
int a{ld}; //错误,信息丢失,精度问题,转换失败
int c(ld), d=ld; //正确
默认初始化
全局变量没有初始化,系统会自动初始化为0,局部变量没有初始化,未定义,会报错。
建议初始化每一个内置类型的变量。
声明:使得名字让程序知道 (不申请存储空间)
定义:创建与名字关联的实体(也就是初始化,需要申请存储空间)
要声明一个变量加 extern,声明变量不能赋值。
extern int i; // 声明 i
int j; // 声明并定义j;
extern int i = 1; // 定义 i,初始化抵消了 extern 的作用。
变量能且只能被定义一次,但是可以被多次声明
c++是静态类型语言,其含义是在编译阶段检查类型。
标识符组成:字母、数字、下划线。不能以数字开头,对大小写敏感。标识符的长度没有限制。
变量命名规范:
实际就是:局部变量覆盖全局变量
复合类型是基于其他类型定义的类型,例如指针和引用
引用就是给对象起别名
引用的本质就是指针常量,指针常量就是指针所指对象的值可以变,但是指针所指的对象不可以变,因此他和一个对象绑定好后,通过引用来改变值,但是不可以在重新绑定到另一个对象上
引用总结
引用必须初始化
引用的初始值必须是一个对象,不能是字面值,如果想要是字面值需要加const
引用不是对象,所以不能定义引用的引用,也不能定义引用的指针
引用必须绑定一个对象上,且不能重新绑定到另一个对象上
int i = 0;
int &r = i;
在块作用域内,指针如果没有被初始化,值将不确定。
指针类型被用于指定它所指向的对象的类型,二者要匹配
int i = 0;
double *dp = &i; // 错误
int *ip = i; // 错误,但 int *ip = 0; 是正确的
int *p = &i; //正确
指针与引用区别
int *p;
int* &r = p; // r是对指针p的引用(指针的引用)
int i = 1;
int &r = i;
int& *p = &r //指向引用的指针,错误的,因为r不是对象,没有地址。
利用解引用符(*)可以访问指针指向的对象。
int i = 42;
int *p = &i;
cout << *p <<endl; 结果为42
空指针
int *p = nullptr; // 推荐这种
int *p = 0;
int *p = NULL; // NULL 是在头文件 cstdlib 中定义的预处理变量,值为 0。
建议初始化所有指针
赋值和指针
int i = 42;
int *p = &i;
*p = 30;
说明:
指针指的是地址,而不是值
void*指针
void* 指针是特殊的指针类型,可以存放任意对象的地址。它的用处比较有限。(不使用)
一条声明语句是由一个基本数据类型和后面的声明符列表组成的。
引用符 & 和指针符 * 都是类型说明符,类型说明符是声明符的一部分。
int i = 1024, *p = &i, &r = i;
int *p, p2; //p是指向int的指针,p2是int
指向指针的指针
int i = 1024;
int *p = &i; //p指向一个int 型数
int **p1 = &p; //p1指向一个int 型指针
指向指针的引用
引用本身不是一个对象,所以没有指向引用的指针,但是指针是对象,存在对指针的引用
int i = 42;
int *p ; //p是int型指针
int *&r = p; //指向指针的引用
-------------------------------------------------------------------------------
int i = 1;
int &r = i;
int& *p = &r //指向引用的指针,错误的,因为r不是对象,没有地址。
想要理解r的类型到底是什么,可以从右往左读
,离变量最近的符号(本例中是&r符号),因此r是一个
引用,声明符以外的部分可以确定
r引用的类型是什么。本例中
&左边是*,所以r引用的是一个
指针`
const对象必须初始化
,因为const对象一旦创建,其值不能改变运算
只在文件内有效
共享const
,必须在定义变量之前添加exten关键字
引用必须初始化
,因此常量引用也必须初始化。常量
也可以是非常量
有俩种情况特殊,下面详细说
int i = 42; //非常量
const int j = 42; //常量
const int &p = i; //正确
const int &p = j; //正确,常量引用的对象既可以是常量也可以是非常量
const int &p = 10; //正确
int &p = 10; //错误
//非常量引用不能指向常量对象
const int p = 66;
int &r = p; //错误
引用的类型必须与其所引用对象的类型一致,有俩种情况特殊,下面详细说
举例
double val = 3.1415;
const int &p = val; //正确
int i =42;
int &r1 = i; //引用r1绑定对象i
const int &r2 = i; //常量引用r2 绑定i,此时不允许通过r2来修改i的值,但是可以用r1来修改i 的值
r1 = 0; //正确,i 的值修改为0
r2 = 0; //错误
指针常量与常量指针区别
可以变
,但是指针所指的地址不可以变
int * const p = &r不可以变
,但是指针所指的地址可以变
const int * p = &r顶层const表示指针常量
,指针本身是一个常量,地址不能变,但是值可以修改。底层const表示常量指针
, 指针所指的值是一个常量,不可以修改,但是地址可以变。顶层const对任意数据类型都适用,但是底层const只用于引用和指针
指针类型既可以是顶层 const 也可以是底层 const,因为引用只能是底层 const,常量引用为底层 const,不存在顶层 const 的引用。
怎么区分是顶层const还是底层const,最简单的方法
记住就能理解了
)const int* const p1 = p2; // 从右向左读,右侧const是顶层const,表明p3是一个常量,左侧const是底层const,表明指针所指的对象是一个常量
const int* p3 = &c; // 这是一个底层const,允许改变 p2 的值(常量指针)----const在左边为底层
int* const p4 = &i; // 这是一个顶层const,不能改变 p1 的值(指针常量)----cosnt在右边为顶层
执行对象的拷贝操作时,不能将底层 const 拷贝给非常量,反之可以,非常量将会转化为常量。
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
字面值属于常量表达式,由常量表达式初始化的 const 对象也是常量表达式。
const int i = 32 //常量表达式
const int j = i + 1 // 常量表达式
const int sum = get_size() //不是常量表达式,尽管 sum 是常量,但它的具体值等到运行时才知道。
constexpr变量
constexpr
类型,使得编译器自动帮我们验证。由 constexpr 声明的变量一定是常量不能是普通函数,必须用常量表达式初始化。
constexpr int sz = size(); //只有当 size() 是一个 constexpr 函数时这才是一条正确的声明语句。
constexpr int mf = 20; //常量表达式
constexpr int limit = mf + 1; // mf + 1是常量表达式
字面值类型
nullptr 或 0 或存储于固定地址的对象
。指针和constexpr
contexpr所定义的对象都是顶层const
,仅对指针有效
const int *p = nullptr; // p 是一个指向整型常量的指针
constexpr int *q = nullptr; // q 是一个指向整数的常量指针
const与constexpr区别
目前有俩种方法来定义类型别名
typedef int wages; // 使用 typedef 关键字
using wages = int; // 使用 using 关键字进行别名声明
typedef wages base, *p; // base 是 double 的别名,p 是 double* 的别名。
指针、常量和类型别名
typedef char* pstring;
const pstring cstr = 0; // 注意:const 是一个指向 char 的常量指针。不能采用直接替换的方式将其理解为 const char* cstr = 0,这是错误的。
auto 必须要有初始值
int vall, sum;
auto item = vall + sum //自动推断为int类型
复合类型、常量和auto
编译器推断出的 auto 类型有时和初始值并不一样,编译器会进行适当的调整:
概括一下就是 auto 会忽略引用与顶层 const。
const int ci = 1, cr = ci;
auto b = ci; // b 是一个普通的 int。
auto c = cr; // c 是一个普通的 int。
const auto d = ci; // d 是一个 const int
auto &e = ci; // e 是一个常量引用(常量引用是底层 const)。注意这个微妙的地方。
auto f = &ci; // f 是一个 const int*(位于左边的 const 是底层 const)
int 与 int *、int & 是一个基本数据类型,而 const int 与 int 不是一种类型。
用 auto 定义引用时,必须用 & 指明要定义的是引用。
该变量的类型(包括顶层 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 是一个引用,必须初始化
decltype和引用
int i = 6, &r = i, *p;
decltype(r+0) b; // b 的类型是 int,因为 r+0 的结果类型是 int。
decltype(*p) c = i; // c 的类型是 int&。
decltype((i)) d = i; // d 的类型是 int&。
struct+类名+类体+分号。类体可以为空。
struct Sales_data{}; //结尾注意有分号
对象定义有俩种方式
struct Sales_data{} students; //方式1
Sales_data students; //方式2
定义类时可以给数据成员提供类内初始值以进行初始化。没有类内初始值的成员则被默认初始化。
类内初始值可以放在花括号中或等号的右边,不能使用圆括号。
预处理器概述
确保头文件多次包含仍能安全工作的常用技术是预处理器。
预处理变量有两种状态:已定义和未定义。一般把预处理变量的名字全部大写。
整个程序中的预处理变量包括头文件保护符必须唯一,通常基于头文件中类的名字来构建保护符的名字,以确保其唯一性。
c++ 中包含三个头文件保护符:
预处理变量无视作用域的规则,作用范围是文件内