C++ Primer Plus拾遗

本博文整理了C++ Primer Plus前六章中的部分知识点,一般为不常用的小技巧或基础概念性的内容。

C与C++的语言特性

C语言特性

  • 结构化编程(Structured Programming):早期语言(如 Fortran 和 Basic )在代码组织上可能会存在问题(例如分支语句常常会让代码逻辑和可读性变得特别差)。对此,C语言使用了结构化编程这一方法,将程序中的分支限制为一组易读的代码结构(例如 for、while、do while 循环以及 if else 语句)。
  • 面向过程编程(Procedure Oriented Programming):以过程为中心的编程思想。以“什么正在发生”为主要目标进行编程
  • 自顶向下( top-down ):将大型程序分解为多个小型、易管理、可重复调用的小任务,这个分解过程可以持续下去,把小任务分解为更小的任务。在C语言中,通过函数来实现“自顶向下”。

C++语言特性

  • 面向对象编程(Object Oriented Programming):在 C++ 中通过类与对象这两种数据结构及封装、多态、继承等方法实现。类规定了用何数据描述一个物像(或者概念)并规定了如何用这些数据去描述,对象则是根据类的要求构造的而成的,代表某个物像(或概念)的特殊数据结构。因此与面向过程不同,面向对象的重点不在任务上,而在于表示概念。面向对象编程常常从低级组织(类)开始构建,进而构建高级组织(程序),该过程称为自下向上编程( bottom-up )
  • 泛型编程(Generic Programming):泛型也是一种特殊的抽象数据结构,相较于面向对象编程关注于数据,泛型编程关注算法本身,因此其不拘泥于数据类型。例如,写一个两数相加返回结果的函数,若输入的数据类型可能为int,也可能为float,则比较麻烦,通过泛型(更具体地说,使用模板)则可较为方便地解决。

可移植性及C++标准

可移植性是指同样的代码,只需要使用针对不同平台设计的编译器进行编译,而不必更改代码本身,就可以跨平台运行(这里所说的平台,是CPU + 操作系统,也就是说,即使操作系统相同,CPU不同,也可能算作不同平台)。然而程序的可移植性的实现有两大阻碍。一是硬件。例如不同的CPU有不同的指令集,这些指令集所构成的汇编语法在不同CPU上可能大相径庭;二是标准。即使是同样的软硬件条件,同一种编程语言由于标准不同语法上可能会有些许差异。

要解决第二种阻碍,可通过制定针对所有平台的统一标准来实现。对此,ISO标准委员会分别于1998、2003、2011年推出了C++标准(分别为C++98、C++03、C++11标准,其中C++03是C++98的补丁版,两者语言特性一致)。之后也ISO也颁布过C++14、C++17等标准。同理,C语言也有国际标准,如C89、C90、C99等(其中C89和C90一样)
虽然C++一般可以视为功能更强大的C,但两者并不能完全兼容。C++标准可“近似地”视为C标准的超集,但也并不是所有的C语言的代码都可正确运行于C++中。

C++ Primer Plus 书中所述基于C++98标准,并介绍部分C++11特性。


整型

分类

C++中的整型有char、short、int、long、long long五种类型(全名分别为short int、long int、long long int,但一般都简写……),每种类型都有有符号版本和无符号版本,因此总共有10种整型。

为了适应日益繁多的字符类型,C++ 还推出wchar_t 类型,C++ 11 标准还加入了char16_t 和 char 32_t 类型。

范围

通过以下头文件可使用几个系统自带的常量,这些常量也清晰地界定了各数据类型的数据范围(Win10 64位环境下):

#include <climits>
//char类型占8位,无符号型为0~255
CHAR_MIN = -128
CHAR_MAX = 127
 
//short类型占16位,无符号型为0~65535
SHRT_MIN = -32768
SHRT_MAX = 32767
 
//int类型占32位,无符号型为0~2^32-1
INT_MIN = -2147483648
INT_MAX = 2147483647
 
//long类型占32位,无符号型为0~2^32-1
LONG_MIN = -2147483648
LONG_MAX = 2147483647
 
//long long类型占64位,无符号型为0~2^64-1
LONG_LONG_MIN = -9223372036854775808
LONG_LONG_MAX = 9223372036854775807

比较有意思的是,如果超过了数据范围上限,则变量值会为最小值,同理,低于下限会变成最大值,即INT_MAX + 1 = INT_MIN,INT_MIN -1 = INT_MAX,无符号型同样有该特性。

按ISO/IEC 14882:2011(即C++ 11标准),无符号类型的char、short、int、long、long long的数据范围都不得低于排在它前面的哪一种数据类型,且标准提到了,具体数据范围可根据环境制定。其中,int型应该为“自然长度”,也就是计算机处理起来效率最高的长度,例如在32位系统中,int就占32位,但在16位系统中,int一般只能占16位。当然也有例外,windows64位环境下,int仍然只占32位。

整型字面值

除了十进制,C++支持用八进制和十六进制表示整数。

八进制:第一位为0,第二位为0到7的数字,则从第二位开始到结束的一整串数字表示一个八进制整数,如012,表示的数字十进制下为11。

十六进制:第一位为0,第二位为“x”或“X”,则从第三位开始到结束的一整串数字表示一个十六进制整数(A~F大小写均可)。如0xF,表示十进制数15。

无论用何种表示方法,在cout的时候统一转换成十进制表示。当然也可用格式控制符指定显示以何种进制显示:

int a = 0xf;
cout<<hex<<a<<endl;    //16
cout<<dec<<a<<endl;    //10
cout<<oct<<a<<endl;    //8

常数后缀

数字常量后可用字母后缀定义其类型,其中,对于整数来说,“u”表示无符号,“l”表示long类型,“ll”表示ll类型。其中,这些后缀顺序随意,大小写不影响,举一些例子:

65536ull
65536llu
999l
999u

后缀体系可以和八进制、十六进制混用。

常数后缀有什么用呢,目前据我所知有以下用途:

  1. 使用auto进行类型推导时(相关知识以后会提到)

  2. 隐式转换。C++中对于整数数字的处理,如果在int范围内就处理为int型,超出则根据平台情况处理为long或long long类型,在用整数数字进行运算时,一般int与int运算,结果默认也为int型,则就会有以下情况产生:

long long b = 2100000000 * 2;

系统会报错,虽然相乘的结果没有超出long long范围,但两个乘数默认处理为int型,相乘结果也默认为int型,则就超了。正确写法:

long long b = 2100000000ll * 2;



浮点型

分为float(单精度)、double(双精度)、long double三种类型,分别为32、64、128位。(和整型数据类型一样,这三种浮点型实际大小也不是全平台固定的,以上结果为Win10系统下所得)

对于float,十进制下有效数字有7位,double则可以达到15位。

浮点型常数表示

可以采用类似科学计数法的方式表示浮点型常数:例如,38.675,可表示为:3.8675e+1,e(E也可以,大小写不限)后面是10的指数,其中指数为正的话加号可以省略。

另外,可以指定浮点型常数的类型。默认情况下为double型,“f”后缀表示float型,“l”后缀为long double型,大小写不限,且可以和上述的科学计数表示法混用。

cout<< 3.0000001f - 3;
cout<< 3.0000001 - 3;

前者输出0,后者输出1e-007,这是因为3.0000001被指定为float型后,由于只能保证7位有效数字的精度,最后的1就被舍掉了。


对数据类型的操作

变量命名

变量名由下划线、字母、数字组合而成,其中不可用数字作为开头。单个下划线可作变量名。

下划线打头的变量是合法的,但不建议用户自定义变量采取这种命名方式。因为“_”、“__”开头的多为系统库函数和宏的名称,用户如此命名自己的变量可能会影响运行的稳定性。

列表初始化

普通变量初始化
int a = {5};

将a初始化为5,其中,“=”可以省略,如果初始化为0,大括号内可以什么都不填。

该初始化方法适用于几乎全部C++自带数据类型:

char c{};
string s{"123"};

此外,使用列表初始化,对于变量类型要求更为严格:不允许缩窄(narrowing),即变量的类型可能无法表示赋给它的值。如:

char a {99999};
char b = 99999;

对b的初始化只是warning,但对于a的初始化就是error了……

此外还有:

int a = 70;
char b {a};

运行的时候虽然只是警告,但也需要注意,在初始化b时,由于a是int型,因此很有可能a中的值会超过b的范围。但相反,给一个int型的变量初始化为一个short型变量的值是被允许的。

数组初始化

和普通变量初始化一样,“=”可以省略,如果初始化为0,大括号内可以什么都不填。

int a[3] {1, 2, 3};
int b[3] {};

同样,数组初始化时每个元素都不允许缩窄:

int a[3] {1.0, 2, 3};  //错误,int不允许小数
char b[3] {'a', 999, '\0'};   //错误,999超出char型范围

对于字符串、字符数组,可:

char c[] {"abcd"};
string s {"abcd"};
结构体初始化

“=”可以省略,大括号内不写东西则全初始化为0,各元素均不得缩窄。

struct Student {
    char name[10];
    int age;
    float score;
};

Student s {"Alice", 15, 89.5};

类型转换

当表达式中含有多个不同的数据类型时,会进行整型提升(integral promotion),bool、char、short及其对应无符号型会被统一转换为int型(bool中的true被转换为1,false被转换为0)。但如果表达式中含有long long类型,则不会作进一步提升。(因此最后运算结果如果超出int范围,即使赋值给long long 型,也会出错)

但如果表达式中涉及到浮点型,则会进一步提升到浮点型,并以最大的类型为准进行提升。举个例子(a的赋值会出错而b不会):

long long a = 2 * 2100000000;
long long b = 2.0 * 2100000000;

强制类型转换:(类型名)变量名或常量,类型名(变量名或常量),举个例子:

float a = 1.8;
cout<<(int)9.8;
cout<<int(9.8);
cout<<(int)a;
cout<<int(a)

输出9,9,1,1。

auto声明

一种特殊数据类型,可以根据变量所赋初始值推断变量类型。因此,C++11标准规定在定义auto变量的同时必须进行初始化。

auto a = 0;     //推断为int型
auto b = 0.0;   //推断为double型



逗号表达式

将多个表达式作为一条语句,常用于for循环之中。
例如:

i++, j--;

逗号表达式按从左到右运算,即先进行i++,再执行j–。逗号表达式本身也有值,其返回最后执行部分(也就是逗号最右边的值),例如:

printf("%d\n", (1,2,3));   //输出3



C++11中的range型循环

对普通数组或vector等的循环,可:

int a[] {1,2,3,4};
for (int i : a)
    cout << i;      //数组输出

for (int &p : a)
    p += 1;         //数组更改

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值