Chinaunix首页 | 论坛 | 博客
  • 博客访问: 190422
  • 博文数量: 28
  • 博客积分: 2060
  • 博客等级: 大尉
  • 技术积分: 293
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-11 19:31
文章存档

2011年(1)

2010年(10)

2009年(17)

我的朋友

分类: C/C++

2010-03-21 21:18:23



题记:本系列学习笔记(C++ Primer学习笔记)主要目的是讨论一些容易被大家忽略或者容易形成错误认识的内容。只适合于有了一定的C++基础的读者(至少学完一本C++教程)。
 
作者: tyc611, 2007-01-18

   本文主要讨论C++表达式,包括各种操作符和类型转换。
   如果文中有错误或遗漏之处,敬请指出,谢谢!
操作符
除法(/)和求模(%)

   如果两个操作数都为正数,除法和求模操作的结果也是正数(或零);
   如果两个操作数都为负数,除法操作的结果为正数(或零),而求模操作的结果则为负数(或零);
   如果两个操作数是一个正一个负,除法和求模的结果取决于机器;求模结果的符号也取决于机器,而除法操作的结果是负数(或零)。
 
   当只有一个操作数为负数时,求模操作结果值的符号可依据分子(被除数)或分母(除数)的符号而定。如果求模的结果随分子的符号定,则除的结果向零一侧取整;如果求模的结果随分母的符号,则除的结果向负无穷一侧取整。

举例:

21 / 6;    // ok: result is 3
21 / 7;    // ok: result is 3
-21 / -8;  // ok: result is 2
21 / -5;   // machine-dependent: result is -4 or -5
21 % 6;    // ok: result is 3
21 % 7;    // ok: result is 0
-21 % -8;  // ok: result is -5
21 % -5;   // machine-dependent: result is 1 or -4


  
短路求值策略(short-circuit evaluation)

   逻辑与(&&)和逻辑或(||)操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定整个逻辑表达式的值时,才会计算其右操作数。
 
移位操作符
 
   移位操作符的操作数的类型必须是整型、枚举类型或者整型提升类型。运算结果的类型是左操作数的提升类型。

   移位操作的右操作数不可以是负数,而且必须是严格小于左操作数位数的值;否则,计算结果是未定义的。

左移:E1 << E2;
   如果E1是unsigned类型,那么其运算结果相当于,E1乘2的E2次幂,然后再对该unsigned类型的个数取模。如果E1是unsigned long型,那么是对 ULONG_MAX + 1 取模;否则,是对 UINT_MAX + 1 取模。(ULONG_MAX和UINT_MAX定义于

右移:E1 >> E2;
   如果E1的类型是unsigned类型,或者E1是signed类型但是非负值,则计算结果相当于,E1除2的E2次幂,然后取结果的整数部分。如果E1的类型是signed类型,且是负值,则计算结果是依赖于实现的。

复合赋值操作符
 
   复合赋值操作符的语法格式形如:a op= b; 其计算结果相当于:a = a op b;

   但前后两种形式是有区别的:使用复合赋值操作时,左操作数只计算了一次;而使用长表达式时,该操作数计算了两次,第一次是作为右值参与op的计算,第二次是作为左值参与赋值计算。

sizeof操作符
 
   sizeof操作符的作用是返回对象或类型名的长度,返回值的类型为size_t,长度的单位是字节。sizeof表达式的结果是编译时常量,具有三种形式:
   sizeof (type name);
   sizeof (expr);
   sizeof expr;
最后一种形式使sizeof更像一个操作符,前两种形式容易使人误解为函数。

操作数的计算顺序
 
   C++中只规定了这四个操作符(&&、||、?:、,)的操作数的计算顺序。除此之外,对于其他操作符并没有规定其操作数的计算顺序。(注:C++也没有规定函数调用时参数的计算顺序)

   操作数的计算顺序引发的问题是C++程序员(包括C程序员)常犯的错误,例如:
   ia[i++] < ia[i]
假如初始i=1,那么这个表达式会有两种可能解释: ia[1] < ia[1] 或者 ia[1] < ia[2]。前面的解释是先计算操作符<的右操作数,后面的解释是先计算操作符<的左操作数。

   另外一个常见的错误就是在同一表达式中分别在不同的子表达式修改和引用变量。例如:a + a++。

   由此可见,正确地理解操作数的计算顺序对编写正确的程序是多么地重要。
 
new和delete

   值初始化()可以在创建内置类型对象时,将其初始化为0;对于类类型,用不用值初始化都将是调用其默认构造函数。例如:
int* pi1 = new int;          // pi1 points to an uninitialized int
int* pi2 = new int();        // pi2 points to an int value-initialized to 0
string* ps1 = new string;    // ps1 points to an initialized empty string
string* ps2 = new stirng();  // ps2 points to an initialized empty string

   如果指针的值为0,则对其进行delete操作是合法的。例如:
   int* p = NULL;
   delete p;    // ok: it's safe
 
悬垂指针 (dangling pointer)

   当删除指针所指对象后,该指针就变为所谓的悬垂指针,其指向了一个已经被删除的对象的位置。

   建议:当删除了指针所指对象后,应当立即将指针置0。
 
内存泄漏(memory leak):指动态分配的内存没有被正常地释放。
 

算术转换
整型提升(integral promotion)
 
   对于所有比int小的整型,包括char、signed char、unsigned char、short和unsigned short,如果该类型所有可能的值都能被int型表示,则它们被提升为int型;否则,它们将被提升为unsigned int型。如果将bool值提升为int型,则false转换为0,true转换为1。
 
   同样,如果unsigned int和long一起参与计算,则当long能表示所有unsigned int时,unsigned int转换为long;否则两个操作数都被转换为unsigned long。
 
指针转换

   指向任何数据类型的指针都可以转换为void*类型;整型常量0可以转换为任意指针类型。
 
bool型转换

   算术值和指针值都可以转换为bool类型。如果指针或算术值为0,则其bool值为false;否则为true。例如:
   double pi = 3.14;
   bool b = pi;        // b is true
 
枚举类型转换

   C++自动地将枚举类型(enum)的对象或其成员转换为整型。将枚举对象或其成员提升为什么类型由机器定义,并且依赖于枚举成员的最大值。无论其最大值是什么,枚举对象或其成员至少提升为int型。如果int型无法表示该枚举成员的最大值,则提升到能表示所有枚举成员值的最小类型(unsigned int、long或unsigned long)。
 
强制类型转换
 
   强制类型转换的语法:cast-name(expr);
其中,cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast之一,type为转换的目标类型,expr为被转换的表达式。

static-cast
   编译器隐式执行的任何类型都可以由static_cast显式完成。
 
dynamic_cast
   可以使用dynamic_cast操作符将基类对象的引用或指针转换为同一继承层次中其他类型的引用或指针。dynamic_cast转换在运行时会进行检查,如果绑定到引用或指针的对象不是目标类型的对象,则dynamic_cast失败。如果转换到指针类型的dynamic_cast失败,则dynamic_cast的结果为0;如果转换到引用类型的dynamic_cast失败,则抛出一个 bad_cast异常。
 
const_cast
   去掉const属性。
 
reinterpret_cast
   为操作数的位模式提供重新解释。如:int* ip; char* pc = reinterpret_cast(ip);
显然这种解释依赖于机器,在做这种转换时一定要清楚自己在干什么。
 
建议:慎用强制转换
   因为强制转换关闭了正常的类型检查。在用强制类型转换时,先考虑是不是设计有缺陷。
 
旧式强制类型转换
 
   旧式强制转换有两种形式:
   type (expr);    // function-style cast notation
   (type) expr;    // C-style cast notation

   保留旧式强制转换是为了对标准C++之前编写的程序保持向后兼容,并保持与C兼容。

   如果文中有错误或遗漏之处,敬请指出,谢谢!

参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998

阅读(1343) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~