chap2 变量和基本类型

来源:互联网 发布:数控编程招聘的视频 编辑:程序博客网 时间:2024/05/14 05:45

Reference:《C++ Primer》学习笔记系列——chap2 变量和基本类型
写在前面的话:为什么要不辞辛劳写这个玩意儿?Ans: C++ Primer 读后感

C++庞大的结构,复杂的细节和众多的特性,很容易看了后面忘记前面。此时需要耐心下来,不会了翻看前面的。重复阅读很有必要,因此第一遍的时候做读书笔记就十分重要了。以后阅读就可以有的放矢,重点看自己没有掌握的地方就可以了。

一、基本内置类型:

2.1. 算术类型(参看笔记)

2.1.2 类型转换

  (1) 当给无符号类型一个超出范围的值时,结果是初始值对无符号的Max取模后的余数。
  (2) 但是给有符号类型一个超出范围的值时,结果是未定义的。程序可能工作、可能崩溃、可能生成垃圾数据

unsigned char = -1;// c的值为255。//负数在计算机中以补码形式存在,即-1=1111 1110 + 1=1111 1111 = 255

(3) 含有无符号类型的表达式
算术表达式中 无符号+-有符号

int i = -42; unsigned u = 10;// -42当成补码 -42 = ~(0010 1010)+1= FFD6 其中若int占32位std::cout<< u + i << std::endl;//算术表达式中既有unsigned,又有int,int会被转换成无符号数。所以输出为:4294967264

小有符号 - 大无符号
当从无符号数中减去一个值时,不管这个值是不是无符号数,都必须确保结果不能是一个负值。

unsigned u1=42,u2=10;std:cout<<u1-u2<<std::endl;//32std::cout<<u2-u1<<std::endl;//结果是取模后的值。10-42=-32=反码+1=FFE0

无符号作为循环变量
控制变量递减时,会陷入死循环,因为无符号数不会为负。解决办法:while代替for。见书中例子;
若表达式里既有signed又有unsigned,则在运算时,会将带符号数自动转换成无符号数,此时含有负数的运算会导致错误结果。如-1*1=4294967295

2.1.3 字面值常量

整型和浮点型字面值 :十进制、##0*八进制,ox##十六进制;浮点型:默认是double类型3.14e5.
这种用后缀来表示具体类型。unsigned ##u; long ##l; long long ##ll; float ##f; long double l;
字符和字符串字面值 :字符串相当于字符数组”hello world!”,这个的长度=个数+1; a’ 字符型常量
通过添加前缀可以指定字符型字面值的类型 u/U/L/u8/
转义序列
布尔字面值和指针字面值:nullptr 是指针字面值

二、变量

​ 数据类型:决定了所占内存的大小和布局方式、值的范围、变量能参与的运算。
列表初始化
​ 其中大括号用于初始化时,若初始值存在丢失风险,则编译器会报错。如果是圆括号,则会省略掉小数值。但是,若变量类型的范围装不下初始化值时,编译器还是会报错。
默认初始化
全局变量会不默认初始化为0;但是局部变量不会被初始化,使用一个未被初始化的变量将会引起错误。类决定了初始化对象的方式,类都自己决定了提供一个怎样的默认值。

int myvarr = 0;int myvarr = {0};int myvarr{0};int myvarr(0);

2.2.2 变量声明和定义的关系

​ C++支持分离式编译,所以将声明和定义区分开。只声明变量:extern int i; 添加extern关键字即可,同时不要显示地初始化变量。变量只能被定义一次,但可以多次被声明 。不能重复定义。
​ C++是一种静态类型语言,即在编译阶段检查类型。若编译器报错,则不会生成可执行文件。

2.2.3标识符

​ 用户自定义的标识符中不能连续出现两个下划线;也不能以下划线紧连接大写字母开头;函数体外的标识符不能以下划线开头。
​ 变量命名规范:变量名——小写字母;类名——大写字母开头;多个单词用大小写或下划线区分。

2.2.4 名字的作用域

​ 全局作用域;块作用域;如果在块内定义了与全局相同名字的变量,则在块内,局部值会覆盖掉全局值。

三、复合类型:引用和指针

​ 引用,给变量起了一个别名;定义时必须要跟变量绑定在一起,一旦绑定就不能再绑定其他对象了。即,定义一个引用,必须初始化。只是一个别名,不会额外分配存储空间引用的类型要跟与之绑定的对象类型严格匹配
int &refVal4 = 10;//错误;初始值必须是一个对象,不能是字面常量

指针

​ 指针定义时无需赋初值;但在块作用域内如果未初始化,将有一个不确定的值
指针中存放某个对象的地址 int *p = &val;//p指针 指向一个int类型的对象不能定义指向引用的指针,因为引用不是对象,没有实际地址
指针与所指向的对象 类型要严格匹配。 可以利用指针访问对象: *P = 0;
空指针:用nullptr来初始化指针,即得到一个空指针;或者:#include <cstdlib> int *p1 = NULL;;或者用0给指针变量赋初值:int *p2 = 0;也可以得到一个空指针。
void* 指针: 可用于存放任意对象的地址。不能直接操作void*所指向的对象,因为不清楚其对象到底是什么类型,可支持什么样的操作。
符合类型的声明:*、&是类型修饰符,一般与变量名字写在一起,不容易混淆,它只修饰这一个变量,不管其后的其他变量。
指向指针的指针int *pi = &val; int **ppi = &pi;
指向指针的引用int *p; int *&r = pi; 从右向左看,r到底是个什么类型

四、const限定符

const对象一旦创建,其值不能改变,因此必须初始化。 const类型可参与的运算,只要不改变其值,就都可以。
​ 默认情况下,const对象仅在文件内有效。要是想要在多个文件中贡献,则定义和声明时都很实用extern。

const 的引用

const int ci =1024; const int &r1 = ci; //声明引用必须也是const int类型,引用于对象的类型必须严格一致。
初始化和对const的引用:初始化常量引用时,允许任意的表达式作为初始值。只要改表达式的值可由转换为引用的类型即可。允许一个常量引用绑定非常量的对象、字面值、甚至是一个一般表达式。

int i=42; const int &r1 = i; const int &r2=42; const int r3 = r1*2;

  对cost的引用肯引用一个并非const的对象:如上述代码。r1绑定了一个变量i. 常量引用仅对引用所参与的操作做出来限定,对于引用的对象本身是不是一个常量未作出限定。

指针和const

指向常量的指针:想要存放常量对象的地址,只能使用这个。const double pi =3.14; const double *cptr = &pi;允许一个指向常量的指针指向一个非常量的对象。这与常量引用类似。
const指针:把指针自己定为常量。常量指针必须初始化,*const说明指针是一个常量,表示存在指针中的那个地址不能再改变了,而不是指向的那个值不变了。

int errNumb =0;int *const curErr = &errNumb; //指向变量的常量指针const double pi =3.1415926; const double *const pip = &pi;//第一个const表示这个指针是指向常量的,第二个cost表示这是个常量指针。即:这是一个 指向常量的常量指针。

顶层const

​     顶层cost: 指针本身是一个常量; 底层const:指针所指的对象是一个常量。
​      用来声明引用的都是底层const,声明变量的cost都是顶层const.

const int ci =42; int i = 42;const int *p2 = &ci;//p2是指向常量的指针const int *const p3 = p2;//指向常量的常量指针p2 = p3;//合法p2 = &i;//合法!!!!因为int* 可以转换为 const int*int &r = ci;//不合法const int &r2 = i;//合法 常量引用可以绑定到一个变量上。
const int v2 = 0; int v1 = v2;int *p1 = &v1, &r1 = v1;const int *p2 = &v2, *const p3 = &i, &r2 = v2;r1 = v2;//合法 拿const int去给int赋值。p1 = p2;//不合法。p2是指向常量的指针,而p1是指向变量的指针。指向常量的指针必须是cost int*类型p2= p1;//合法。允许指向常量的指针指向非常量对象p1 = p3;//p3是指向常量的指针,必须要cost int*类型;而p2是指向变量的p2 = p3;//合法。底层const是一样的。

constexpr和常量表达式

​     常量表达式在编译过程中就能得到计算结果。
    constexpr可以声明一个变量是常量,而且必须用常量表达式初始化。constexpr int sz = size()只有当size()函数声明为constexpr函数时,这才是一条正确的声明语句。这样在编译时就能得到结果。
constexpr能定义的数据类型有限,全局变量都是存放在固定地址的,故可以定义为constexpr类型,比如初始化constexpr指针等,但函数体内局部变量的地址不是固定的,就不能这么定义。
指针和constexpr:
​ 一个cosntexpr指针的初始值必须是nullptr或者o,或者是存储于某个固定地址中的对象。constexpr把它所定义的对象置为了顶层const.

五、处理类型

类型别名

typedef A,B;声明B是A的同义词
using B = A;别名声明B时A的同义词

typedef char *pstring;const pstring cstr = 0;//这里cstr是指向char的常量指针!!!!容易搞错const pstring *ps;//ps是一个指针,它的对象指向char的常量指针

auto类型说明符

​   auto让编译器通过初始值去推算变量的类型。auto在一条语句中声明多个变量时,只能有一个数据类型。所以所有的变量的初始数据类型必须一样。
auto一般忽略顶层const,但保留底层const

decltype类型指示符

​   希望从表达式的类型去定义新的变量,而不是要其值。编译器分析表达式并得到它的类型,却不实际计算其值。decltype会返回顶层const和引用,也包括底层const

int *p = &i;//p是指向int的整型指针decltype(*p) c;//cint & 类型,必须初始化

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

  如果表达式加了多层括号,将得到引用类型。 decltype((i)) d;//错误。这个是引用类型,必须初始化

自定义数据结构

预处理器概述

#ifndef SALES_DATA_H//这些预处理命令是为了防止重复包含#define SALES_DATA_H#include <string>struct Sales_data{    std::string bookNo;    unsigned units_sold = 0;    doulbe revenue = 0.0;}#endif
原创粉丝点击