如果您无法下载资料,请参考说明:
1、部分资料下载需要金币,请确保您的账户上有足够的金币
2、已购买过的文档,再次下载不重复扣费
3、资料包下载后请先用软件解压,在使用对应软件打开
下载下载第8章内联函数C+++C继承C的一个重要特性是效率。假如++C的效率显著地比C低,程序设计者不会使用它。在C中,保护效率的一个方法是使用宏)orcam(。宏可以不用普通函数调用就使之看起来像函数调用。宏的实现是用预处理器而不是编译器。预处理器直接用宏代码代替宏调用,所以就没有了参数压栈、生成汇编语言的LLAC、返回参数、执行汇编语言的NRUTER的时间花费。所有的工作由预处理器完成,因此,不用花费什么就具有了程序调用的便利和可读性。C+++C中,使用预处理器宏存在两个问题。第一个问题在C中也存在:宏看起来像一个函数调用,但并不总是这样。这就隐藏了难以发现的错误。第二个问题是++C特有的:预处理器不容许存取私有)etavirp(数据。这意味着预处理器宏在用作成员函数时变得非常无用。为了既保持预处理器宏的效率又增加安全性,而且还能像一般成员函数一样可以在类里访问自如,++C用了内联函数(inlinefunction)。本章我们将研究++C预处理器宏存在的问题、++C中如何用内联函数解决这些问题以及使用内联函数的方针。8.1预处理器的缺陷预处理器宏存在的关键问题是我们可能认为预处理器的行为和编译器的行为一样。当然,有意使宏在外观上和行为上与函数调用一样,因此容易被混淆。当微妙的差异出现时,问题就出现了。考虑下面这个简单例子:#definef(x)(x+1)现在假如有一个像下面的f的调用f(1)预处理器展开它,出现下面不希望的情况:(x)(x+1)(1)出现这个问题是因为在宏定义中f和括号之间存在空格缝隙。当定义中的这个空格取消后,实际上调用宏时可以有空格空隙。像下面的调用:f)1(依然可以正确地展开为:(1+1)上面的例子虽然微不足道但问题非常明显。在宏调用中使用表达式作为参数时,问题就出现了。存在两个问题。第一个问题是表达式在宏内展开,所以它们的优先级不同于我们所期望的优先级。例如:#definefloor(x,b)x>=b?0:1现在假如对参数使用表达式第8章内联函数143下载if(floor(a&0x0f,0x07))//...宏将展开成:if(a&0x0f>=0x07?0:1)因为&的优先级比=>的低,所以宏的展开结果将会使我们惊讶。一旦发现这个问题,可以通过在宏定义内使用括弧来解决。上面的定义可改写如下:#definefloor(x,b)((x)>=(b)?0:1)发现问题可能很难,我们可能一直认为宏的行为是正确的。在前面没有加括号的版本的例子中,大多数表达式将正确工作,因为=>的优先级比像+、/、--甚至位移动操作符的优先级都低。因此,很容易想到它对于所有的表达式都正确,包括那些位逻辑操作符。前面的问题可以通过谨慎地编程来解决:在宏中将所有的内容都用括号括起来。第二个问题则更加微妙。不像普通函数,每次在宏中使用一个参数,都对这个参数求值。只要宏仅用普通变量调用,这个求值就开始了。但假如参数求值有副作用,那么结果可能出乎预料,并肯定不能模仿函数行为。例如,下面这个宏决定它的参数是否在一定范围:#defineband(x)(((x)>5&&(x)<10)?(x):0)只要使用一个“普通”参数,宏和真的函数工作得非常相像。但只要我们松懈并开始相信它是一个真的函数时,问题就开始出现了。下面是这个程序的输出,它完全不是我们想从真正的函数期望得到的结果:144++C编程思想下载当a等于4时,测试了条件表达式第一部分,但它不满足条件,而表达式只求值一次,所以宏调用的副作用是a等于5,这是在相同的情况下普通函数调用得到的结果。但当数字在范围之内时,两个表达式都测试,产生两次自增操作。产生这个结果是由于再次对参数操作。一旦数字出了范围,两个条件仍然测试,所以也产生两次自增操作。根据参数不同产生的副作用也不同。很清楚,这不是我们想从看起来像函数调用的宏中所希望的。在这种情况下,明显有效的解决方法是设计真正的函数。当然,如果多次调用函数将会增加额外的开销并可能降低效率。不幸的是,问题可能并不总是如此明显。我们可能不知不觉地得到一个包含混合函数和宏在一起的库函数,所以像这样的问题可能隐藏了一些难以发现的缺陷。例如,在H.OIDTS中的putc()宏可能对它的第二个参数求值两次。这在标准C中作了详细说明。宏toupper()不谨慎地执行也会对第二个参数求值超过两次。如在使用toupper(*p++)[1]时就会产生不希望的结果。宏和访问当然,对于C需要对预处理器宏谨慎地编码和使用。即使不是因为宏不是成员函数所需要的范围概念这一原因,我们也会在++C中避免使用它所带来的麻