您的位置:首页 > 编程语言 > C语言/C++

重读C++primer 第三版(二)

2013-09-24 17:19 399 查看
第二部分

主要内容为第二篇 基本语言

1,字节(byte)和字(word)。在32位系统下,一个字节由8位(bit)组成,而一个字由32位或者说4字节组成,并且,字是机器相关的,不同计算机中的字长可能不一样。现在系统正在朝64位系统转换,64位系统里,字长通常为8字节。

2,基本数据类型长度。

    ​字符型char,通常用来表示单个字符和小整数,一般占用一个机器字节

    ​整形int,short,long,用来表示不同长度的整数值,通常情况下,short以半个字表示,int以一个字表示,而long则由一个或两个字表示,在32位系统中,int和long的长度通常是相同的。

    ​浮点型float,double,long double,用来表示单精度浮点数,双精度浮点数和扩展精度的浮点数,通常情况下,float为一个字,double为两个字,long double为三个或四个字。

3,整形文字常量。在整形文字常量前面加一个0,该值将被解释成八进制数,而在前面加0x或0X,该值将被解释成16进制。缺省情况下,整形文字常量被当作一个有符号的int型,可以在它后面加上L或者l将其指定为long型,还可以在它后面加上U或者u将其指定为无符号数,并且可以同时使用,顺序无关。

4,浮点型文字常量。浮点型文字常量可以以科学计数法或者普通10进制数来表示。使用科学计数法,指数可以写作e或者E。缺省情况下,浮点型文字常量的类型是double型,可在其后加f或者F将其指定为单精度类型,也可以在其后加l或者L将其指定为扩展精度类型。需要注意的是f、F、l、L只能用在十进制形式中。

5,bool型文字常量。true or false。

6,文字字符常量。可打印的文字字符常量以单引号括起来表示,不可打印的文字字符常量用特殊的转义序列来表示。而在更一般的情况下,转义序列采用\ooo的格式来表示,ooo表示的是三个八进制数字组成的序列,此八进制序列的值代表的是该字符在机器字符集里面的数字值。在文字字符常量前加L可将其指定为宽字符(wchar_t)类型。

7,字符串型文字常量。由零个或多个用双引号括起来的字符组成。不可打印的字符可由相应的转义序列来表示,一个字符串文字常量可以扩展到多行,在一行的最后加上一个反斜杠表明字符串文字在下一行继续。字符串型文字常量的实际类型是常量字符数组,它由字符串文字本身和编译器自动加上的结束符组成。在字符串型文字常量前加L可将其指定为宽字符型文字常量,它的实际类型是宽字符数组,当然,它也有一个等价的宽空字符作为结束符。如果两个以上的字符串型文字常量或者宽字符串型文字常量在程序中相邻,C++会把它们连接在一起,并在最后加上一个结束符。但是如果把一个字符串和宽字符串连接在一起,将会是未定义(undefined)行为。

8,变量。变量是有名字的内存存储区,可以通过程序对其进行读写等操作。变量和文字常量都有类型,区别在于变量是可寻址的(addressable),而且,每一个变量都对应两个值:它的数据值,它存储在内存地址中,通常被称为右值(rvalue),可以理解为被读取的值(read value),文字常量和变量都可以做右值;它的地址值,即存储数据值的那块内存的地址,通常被称为左值(lvalue),可以理解为位置值(location value),文字常量不能作左值。

9,关于数据指针(区别于函数指针)。不同数据类型指针之间的区别不是在指针的表示上,也不是在指针所持的值(地址)上,对于数据指针来说,这两点都是相同的,不同之处在于指针所指对象的类型上。指针的类型可以指示编译器怎么解释特定地址上内存的内容,以及该内存区域应该跨越多少个单元。

10,指针的算术运算。指针可以让它的地址值增加或减少一个整数值,这个整数值等于若干个该指针类型的倍数。但是指针的算术运算最好是用在数组上面,因为不能保证若干个相同类型的变量连续存储在内存中。

11,关于const指针。例:const int *conp;和int const *conp一样,都表示该指针指向的是一个常量,指向的对象值不可修改,但其指向可以修改。而int *const pcon=0;表示的是一个指向地址不可修改的指针,所以,在定义的时候就要对其赋值,但其指向的对象值可以修改。将两者结合起来,就可以表示指向const对象的const指针,如:

    ​const int i = 10;

    ​const int *const conp = &i;

另外为了防止通过指针修改常量对象,规定试图将一个非const对象指针指向一个常量对象的动作是非法的,这就是为什么 

    ​const char *pTest = "Hello World!"; 

    ​char *pTmp = pTest;

    ​编译报错的原因。

12,关于引用。引用是一种没有指针语法的特殊指针。一旦定义了引用之后就不能再指向其它对象,所以,它必须在定义的时候初始化,对引用的操作都被应用到它所指的对象身上。它不能像对指针那样用一个对象的地址初始化,比如:

    ​int ival = 1024;

    ​int &refVal = &ival;//错误,refVal是int类型,而非int *类型

但是可以定义指针引用,如:

    ​int *pi = &ival;

    ​int *&ptrRef = pi;

因为这个特性,C++很少使用指向独立对象的引用类型,而是主要用在函数的形式参数中。

引用和指针的主要区别:引用自定义好之后就只能指向一个对象,不能再改变它的指向,而指针则可以(const指针除外),还有一个区别是引用之间的赋值改变的是被引用的对象,而非引用本身,而指针之间赋值改变的就是指针本身。如:

    ​int val1 = 1024;

    ​int val2 = 2048;

    ​int *pval1 = &val1;

    ​int *pval2 = &val2;

假设有如下代码:

    ​pval1 = pval2;

则两个指针指向的是同一个对象val2,而引用之间赋值则有区别,如:

    ​int &refval1 = val1;

    ​int &refval2 = val2;

    ​​refval1 = refval2;//这里被改变的是val1,即val1变成2048,但refval1本身的值不变,仍然指向val1。

另外,不允许非const引用指向需要临时对象的对象或值。有点绕口,例:

    ​const int &refConst = 1024;

这样定义是合法的,但如果不是const对象将会导致编译错误。这是因为,引用在内部存放的是一个对象地址,引用是这个对象的别名,对于不可寻址的值,如文字常量,编译器为了实现引用,必须生成一个临时对象,引用实际上指向是该临时对象,但用户不能访问它,这时如果给引用赋值,改变的是临时变量,而非真正的文字常量,也就是说赋值操作不生效,比较蛋疼,而const引用因为是只读的,所以,不能修改它的值。

13,bool类型。当表达式需要一个算术值时,bool对象和bool文字常量都被隐式提升为int型,false变成0,true变成1。算术值或指针值也能转换成bool型值,0或空指针为false,其余都为true。

14,关于数组。数组标识符代表数组第一个元素的地址,它的类型是数组元素类型的指针。

15,关于typedef。typedef并没有引入新类型,而只是为现有类型引入一个助记符。需要注意的是,不能简单吧typdef当成宏定义展开,比如:

    typedef char *cstring;

    extern const cstring cstr;

这时,cstr的类型并不是const char *,而是char *const,也就是指向字符的const指针,而不是指向const字符的指针。这是因为,const修饰的是cstr的类型,而cstr是char*类型,它是个指针,所以,cstr其实是一个const指针,而非想当然的const char *。

16,关于volatile。当一个对象的值可能会在编译器的控制或监控之外被改变时(例如一个被系统时钟更新的变量),该对象应该被声明成volatile。volatile的主要目的是提示编译器,该对象的值可能在编译器未监测到的情况下改变,编译器不能武断在对引用这些对象的代码作优化处理。

17,关于pair类型。pair类型也是标准库的一部分,它可以在单个对象内把相同或不相同类型的两个值关联起来,然后可以使用成员访问符(member access notation)访问pair中的单个元素,它们的名字为first和second。例:

    #include<utility>

    std::pair<std::string, std::string> pair_test("first", "second");

    std::cout<<"Pair's first value:"<<pair_test.first<<std::endl;

    std::cout<<"Pair's second value:"<<pair_test.second<<std::endl;

18,算术操作符相关。两个整数相除的结果是整数,小数部分被截掉。%操作符只能用在整值类型(char,short,int,long)上,如果两个操作数为正,结果为正,如果其中有一个为负,则结果是机器相关,未定义的,移植性无法保证。

19,等于、关系和逻辑操作符相关。等于、关系和逻辑操作符的运算结果是布尔常量,当它们用在要求整数值的上下文环境时,它们的结果将被提升成1(true)或0(false)。二元关系操作符的潜在缺点:左右操作数的计算顺序在C/C++中都是未定义的,因此计算过程必须是顺序无关的,比如:

    ​​if(ia[index++] < ia[index])

这种表达式,结果就是未定义的,具体可参考C标准规范的副作用和序列点。

20,赋值操作符相关。每个被赋值的操作数都是相同的数据类型,赋值操作符可以连接在一起。如:

    int ival, jval;

    ival = jval = 0;

ival和jval都被赋值为零,但:

    int ival,*pval;

    ival = pval = 0;

尽管ival、pval都能被赋值为零,但是ival和pval类型不同,所以,是非法的。

21,sizeof相关。用在char上时,所有的C++实现结果都是1。sizeof操作符在编译时刻计算,因此,可以看作常量表达式。

22,逗号表达式相关。逗号表达式是一系列由逗号分开的表达式,这些表达式从左到右依次计算,逗号表达式的结果是最右边表达式的值。

23,操作符优先级。操作符具有优先级和结合性。赋值操作符为右结合性,算术操作符为左结合性。应该尽量使用括号来明确程序员的优先级意图。

24,隐式类型转换相关。隐式类型转换由编译器进行,主要有以下几种情况:

    *在混合类型的算术表达式中,最宽的数据类型成为目标转换类型,也就是说小类型会被提升为大类型。

    *用一种类型赋值给另外一种类型对象,目标转换类型是被赋值的对象类型。

    *把一个表达式传递给一个函数调用,目标转换类型是形式参数的类型。

    *从一个函数返回一个表达式,目标转换类型是函数的返回类型。

25,算术转换相关。算术转换保证二元操作符的两个操作数被提升为共同类型,然后再用这个类型表示它的结果类型。转换规则如下:

    *为防止精度丢失,类型总是被提升为较宽的类型。

    *所有含有小于整形的有序类型的算术表达式,在计算之前,其类型都会被转换成整形。

26,强制类型转换相关。C++的强制类型转换包括C时代的旧强制类型转换和新强制类型转换。其中,新的强制类型转换如下:

    *const_cast。用于转换掉表达式的常量性以及volatile的volatile特性。

    *static_cast。相当于隐式类型转换。但是使用它的时候,转换时的编译器告警信息都会被关闭。

    *reinterpret_cast。用于对操作数的位模式执行一个比较低层次的重新解释。它其实相关于C时代的旧强制类型转换。

    *dynamic_cast。用于在运行时刻识别由指针或引用指向的类对象。比如从子类指针转换到父类指针等。    

27,关于switch语句。把一条声明语句放在与case或者default相关联的语句中是非法的,除非把它放在一个语句块中。

28,循环语句相关。do-while循环的条件部分不支持对象定义。其余循环结构可在条件部分定义对象,并且在标准C++中,该对象的生命周期是循环语句,在循环之外,该对象不可见,在标准C++出现之前的某些编译器中,该对象在循环之外也是可见的,比如VC6.0这个老古董。

29,防止类对象被赋值的方法:将该类的拷贝构造函数和拷贝赋值操作符声明为私有成员,并且不提供它们的定义。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++primer