9.4 合法但不可移植的代码
C++(www.cppentry.com)继承了C语言的许多实现性定义(implementation-defined)的行为和未经指定的(unspecified)行为,并增加了一些新的行为。虽然C++(www.cppentry.com)程序可以合法地依赖于实现性定义行为和未经指定行为,但这么做将会导致这部分程序代码不能获得最大化的可移植性。遗憾的是,我们却经常会不自觉地编写具有这些依赖性的代码。
9.4.1 实现性定义的行为
C++(www.cppentry.com)语言拥有40多个实现性定义结构。例如,对于右移运算符(>>),当它是确定的(见9.3节),并且第一个操作数是有符号类型的负值时,这个操作的结果就是实现定义行为的。这个操作可能是一个逻辑右移操作(最高位用0来填充),也可能是一个有符号的右移操作。对于程序中每一个实现性定义结构,C++(www.cppentry.com)编译器实现都需要指定它实际要实现的特定行为。因此,由于C++(www.cppentry.com)编译器的差异,包含这类结构(指上面的实现性定义结构)的程序的行为往往就有所差异。
譬如,char类型究竟是有符号类型的还是无符号类型就是实现性定义的,考虑下面的代码:
- char c;
- //...
- c >>= 1;
如果char是无符号类型的,或者operator>>被定义为逻辑右移运算符,那么上面代码将会清除c的符号位;如果char是有符号类型,并且operator>>被定义为算术右移运算符,那么c的符号位会继续存在(即不会被清除)。
ARM里已经给出了完整的实现性定义行为的集合,ANSI/ISO也给出了这个集合。遗憾的是,当程序的构建依赖于这些实现性定义行为的时候,C++(www.cppentry.com)编译器并不会告诉程序员这一潜在的陷阱。因此,这幅沉重的担子自然就落在希望编写可移植代码的程序员身上,程序员必须以非常小心谨慎的态度避免编写含有这类结构的代码。