《C++Primer》读书笔记(二)C++基础(上)

来源:互联网 发布:moe软件官网 编辑:程序博客网 时间:2024/06/04 19:26

2.1 基本内置类型

算术类型

(1)算术类型分为:整型(包括字符、布尔)、浮点型
- 算术类型所占的最小尺寸,如下:

类型 最小尺寸 bool 未定义 char 8 bit int 16 bit long 32 bit float 6位有效数字 double 10位有效数字

其中,一个char的大小和一个机器的字节一样
- char16_t 是 Unicode 字符(Unicode 是用于表示所有自然语言中字符的标准),另外,还有 char32_t
- C++语言规定,一个int至少和一个short一样大,一个long long至少和一个long一样大

*内置类型的机器实现*
- 可寻址的最小内存块称为字节(byte)
- 存储的基本单元为字(word),它通常由几个字节组成
- 大多数计算机将内存中的每个字节与一个地址关联,为了赋予内存中某个地址明确的含义,必须首先知道存储在该地址的数据的类型。类型决定了数据所占的比特数以及如何解释这些比特的内容

带符号类型和无符号类型

(1)除去布尔型和扩展的字符型之外,其他整型可以分为 signed 和 unsigned 两种

(2)char实际上会表现为有或者无符号的,由编译器决定

(3)在表示范围内正值和负值的量应该平衡,例如:8比特的signed char 理论上表示-127到127,而大多数计算机将表示范围定为-128到127

*选择类型的建议*
1. 明确知晓数值不为负,则用无符号类型
2. 一般用int、double进行运算
3. 算术表达式一般不用char或者bool,因为不同机器可能所带符号不同

类型转换

(1)强行赋值转换情况

初始值 赋值给 结果 非 bool bool 初始值为0,则false;否则true bool 非bool 初始值为false,则0;否则1 浮点 整数 保留整数部分 整数 浮点 小数部分记为0(整数所占空间若超浮点容量,可能会损失) 超出范围的值 无符号 初始值对无符号类型表示数值总数取模后的余数 超出范围的值 符号类型 undefined

- 避==免无法预知==和==依赖于实现环境==的行为

(2)含有无符号int又有符号int时,会将有符号int转换成无符号int

unsigned u=10;int i=-42;std::cout<<u+i<<std::endl; // 如果int占32位,输出4294967264
  • 其中,相加前把-42转换成无符号数,把==负数转换成无符号数==类似于直接给无符号数赋负值,结果是等于这个负数加上无符号数的模

(3)死循环的例子

for(unsigned u=10;u>=0;--u)   std::scout<<u<<std::endl;

*切记混用带符号类型和无符号类型*

练习

1.阅读代码(32位)

unsigned u=10,u2=42;int i=10,i2=42;std::cout<<u-u2<<std::endl;std::cout<<"/"<<std:endlstd::cout<<i-u2<<std::endl;
  • 输出结果为:2^32-32 / 2^32-32

字面值常量

(1)整型:(0开头表示八进制,0x或0X表示十六进制)
- 20(十进制)
- 024(八进制)
- 0x14(十六进制)

(2)浮点型(小数,或科学计数法(指数部分用E或e标识))
- 3.14159
- 3.14159E0
- 1e0
- 浮点字面值默认为double,使用其他浮点需要加后缀

(3)字符和字符串字面值
- ‘a’ //字符
- “Hello World” //字符串
- (字符串实际为常量构成的数组(array),因此字符串的长度比内容多1,为结尾处的‘\0’)

(4)转义序列(等等)

功能 序列 换行符号 \n 横向制表符 \t 退格符 \b

(5)指定字面值的类型
- 字符和字符串——前缀
- 整型和浮点——后缀

(6)布尔字面值和指针字面值
- true和false
- nullptr(指针字面值)

练习

1.利用转义序列编程,要求先输出2M,然后转到新一行。修改程序使其输入2,然后输出制表符,再输出M,最后转到新一行。

std::cout<<"2M"<<std:endl;std::cout<<"\n"<<std::endl;std::cout<<2<<"\t"<<'M'<<std::endl;

2.2 变量

(1)变量定义基本形式
- 类型说明符(type specifier),紧跟一个或多个变量名
- 字符串类型在标准库std中:std::String

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

(2)初始值

初始化:创建变量时赋予其一个初始值
赋值:把对象当前值擦除,以一个新值来替代

(4)默认初始化
- 定义变量时没有指定初值,则变量被默认初始化
- 定义在函数体内部的内置类型变量将不被初始化。一个未被初始化的内置类型变量的值是未定义的,如果试图访问将引发错误

定义于函数体内的内置类型的对象如果没有被初始化,则其值未定义。
类的对象如果没有显式初始化,则值由类确定

==建议初始化每一个内置类型的变量==

变量声明和定义关系

(1)C++支持分离式编译,即允许将程序分割为若干文件,每个文件独立编译

(2)声明(declaration)使得名字为程序所知

(3)定义(definition)负责创建与名字关联的实体

==任何声明并显式初始化即成为定义==

C++是一种静态类型语言,含义是在编译阶段检查类型(成为类型检查)

标识符(字母、数字和下划线组成,数字不能做开头)

(1)约定俗成的命名规范
- 标识符要体现实际含义
- 变量名一啊不能用小写字母
- 用户自定义的类名一般以大写字母开头
- 如果标识符由多个单词组成,要有明显区分,如student_loan

(2)自定义标识符注意:
- 不能连续出现两个下划线
- 不能下划线紧连大写字母开头
- 函数体外的标识符不能以下划线开头

名字的作用域

(1)main定义于所有{}之外,有==全局作用域==

(2)定义于main{}中的名字,值能在main所在块有效,有==块作用域==

建议:当你第一次是用变量时,才定义它(便于找到变量的定义,初始化更合理)

(3)优先使用正在作用域内的名字,要区分可以对外层使用(::reused)

2.3 复合类型

引用(reference)

int ival=1024;int &refVal=ival; //refVal是ival的另一个名字
  • 定义引用时,程序把引用和它的初始值绑定在一起,而不是拷贝给引用
  • 引用即别名

练习

1.下列那些定义不合法?

int ival=1.01;int &rval=1.01; //不合法int &rval2=ival; //不合法int &rval3 //不合法

2.执行下面代码,会什么结果?

int i,&ri=i;i=5;ri=10;std::cout<<i<<" "<<ri<<std::endl;
  • 将会输出:10 10

指针

指针是“指向”另外一种类型的复合类型

与 引用 之不同

==指针本身就是一个对象==
- 允许对指针赋值和拷贝
- 在指针的生命周期内,可以先后指定几个不同对象

==指针无需在定义时赋初值==
- 与内置类型一样,指针没有被初始化,也将拥有一个不确定的值

(1)指针定义:

int *ip1,*ip2; //都是直线int型对象的指针double dp,*dp2; //dp2是指向double型对象的指针,dp是double型对象

(3)获取对象的地址
- 指针存放某个对象的地址,要获取,要使用取地址符(操作符&)

int ival=43;int *p=&ival;

其中,p存放ival的地址,或者说:p是指向ival的指针

> 因为引用不是对象,所以不能定义指向引用的指针

(4)指针的值(下列只有第一个能访问和拷贝)
- 指向一个对象
- 指向==近邻对象所占空间的下一个位置==
- 空指针
- 无效指针

(5)获取指针访问对象
- 如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问

关键概念:多重含义的符号 * 和 &

(6)空指针
- 空指针的生成办法(可以用于检查指针是否为空)

int *p1 = nullptr;int *p2 = 0;int *p3 = NULL; //预处理变量,不属于命名空间std

- 一般用nullptr,因为可以nullptr可以转换成任意其他的指针类型

(7)赋值和指针
- 给指针赋值,就是令它存放一个新的地址

(8)void* 指针
- 一种特殊的指针类型,可以用来存放任意对象的地址(但是我们不值得所指对象的类型)

练习

1.下列代码的作用

int i=42;in *p1=&i;*p1=*p1**p1;
  • p1作为指针变量,存放i的地址,随后*p1即是i,最后相当于:i=i^2

2.下列定义是否非法?

int i=0;double* dp=&i; //不合法,double的指针不能存int的地址int *ip=i; //不合法,int不能赋值给指针int *p=&i; //合法

3.下列代码,为什么p合法而lp非法?

int i=42;void *p=&i;long *lp=&i;
  • 因为void*指针可以存放任意类型指针;long指针不能存放int的地址

理解复合类型的声明

(1)变量的定义:基本数据类型+一组声明符
- 虽然基本数据类型只有一个,但是声明符却可以不同

int i=1024,*p=&i,&r=i;
  • 注意:定义多个变量
int* p,r; // p是指向int的指针,r是int

(2)指向指针的指针

int ival=1024;int *pi=&ival;  // pi指向一个int型数int **ppi=&pi;  //ppi指向一个int型指针

(3)指向指针的引用

int i=42;int *p;  //p是一个int型的指针int *&r=p;  //r是一个对指针p的引用r = &i;  //r引用了一个指针,因此给r赋值&i就是令p指向i*r = 0;  //解引用r得到i,也就是p指向的对象,将i的值改为0

练习

1.说明下列变量的类型和值
- int* ip , i , &r = i ; // ip为int指针,i为int数,r为i的引用
- int i,*ip=0; // i 为int数,ip为int型的空指针
- int* ip,ip2; // ip为int指针,ip2为int数

const限定符

有时我们希望定义这样一种变量,它的值不能改变。

const int bufSize=512;
  • const对象必须立即初始化

(2)默认状态下,const对象仅在文件内有效
- 解决办法:(声明和定义都添加extern)

extern const int bufSize = fcn(); //在file.cc定义extern const int bufSize; //与file.cc中的bufSize是同一个

(3)const的引用——==常量引用是对const的引用==
- 常量的引用,不过是绑定别名给一个const,仍然不能用引用来修改其值

(4)初始化和对const引用

int i=42;const int &i1=i; //合法const int &r2=42; // 正确const int &r3=r1*2; //正确int &r4=r1*2; //非法

(5)对const的引用可能一个非const对象

int i=42;int &r1=i;  //引用r1绑定对象iconst int &r2=i;  //r2也绑定对象i,但是不允许通过r2修改i的值r1=0;  //r1并非常量,i的值修改为0r2=0;  //错误:r2是一个常量引用

指针和const

(1)指向常量的指针不能用于改变其所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针

const double pi=3.14;  //pi是常量,值不能改变double *ptr=&pi;  //错误,ptr是一个普通指针const double *cptr;  //正确,cptr可以指向一个双精度常量

tips:所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向了常量,所以自觉地不去改变所指对象的值

(2)const指针

int errNumb=0;int *const curErr = &errNumb; //curErr将一直指向errNumb

==从右向左读,依然是读懂指针等复杂声明的好办法==

练习

《C++ Primer》 ,57页,题目未做,待日后补充

顶层const

指针本身是一个对象,它又可以指向另外一个对象

因此,指针本身是不是常量以及指针所指的是不是一个常量,是两个独立问题。
- 顶层const:表示指针本身是一个常量
- 底层const:表示指针所指的对象是一个常量

int i=0;int *const p1=&i; // 不能改变p1的值,这是一个顶层constconst int ci=42; // 不能改变ci的值,这是一个顶层constconst int *p2=&ci; // 允许改变p2的值,这是一个底层constconst int *const p2=p2; //靠右的const是顶层const,靠左的是底层constconst int &r=ci; // 用于声明引用的const都是底层const

当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显,其中,顶层const不受什么影响

i=ci; //正确:拷贝ci的值,ci是一个顶层const,对此操作无影响p2=p3; //正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响

练习

1.对于下面的语句,说明其顶层还是底层const?

const int v2=0;int v1=v2;int *p1=&v1,&r1=v1;const int *p2=&v2,const p3=&i,&r2=v2;

2.此题未做

constexpr和常量表达式

是指值不会改变并且在编译过程就能得到计算结果的表达式

(1)constexpr变量

==一般来说,如果你认定变量是一个常量表达式,那就把它声明成constexpr类型==

(2)字面值类型
- 算术类型、引用、指针都属于字面值类型
- 自定义类、io库、string等不属于字面值类型,即不能定义为constexpr

(3)指针和constexpr
- 在constexpr声明定义一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关

const int *p=nullptr; //p是一个指向整型常量的指针

练习

1.下面代码是否合法?
“`
int null=0,*p=null;
···

0 0
原创粉丝点击