一、#if 后面的表达式:
#define VERSION 2
#if defined x || y || VERSION < 3
1 . 首先处理defined运算符,defined运算符一般用作表达式中的一部分,如果单独使用,#if
defined x相当于#ifdef x ,而#if !defined x相当于#ifndef x。在这个例子中,如果x 这个
宏有定义,则把definedx替换为1 ,否则替换为0 ,因此变成#if 0 || y || VERSION < 3。
2 . 然后把有定义的宏展开,变成#if 0 || y || 2 < 3 。
3 . 把没有定义的宏替换成0 ,变成#if 0 || 0|| 2 < 3 ,注意,即使前面定义了一个变量名
是y ,在这一步也还是替换成0 ,因为#if 的表达式必须在编译时求值,其中包含的名字只能是
宏定义。
4 . 把得到的表达式0 || 0 || 2 < 3像C 表达式一样求值,求值的结果是#if 1,因此条件成立
assert 是头文件assert.h 中的一个宏定义,执行到assert(is_sorted())这句时,如
果is_sorted() 返回值为真,则当什么事都没发生过,继续往下执行,如果is_sorted()返回值为假
(例如把数组的排列顺序改一改),则报错退出程序:
main: main.c:33: binarysearch: Assertion`is_sorted()' failed.
Aborted
在代码中适当的地方使用断言(Assertion)可以有效地帮助我们测试程序。也许有人会问:我们用
几个测试函数来测试binarysearch,那么这几个测试函数又用什么来测试呢?在实际工作中我们要
测试的代码绝不会像binarysearch这么简单,而我们编写的测试函数往往都很简单,比较容易保证
正确性,也就是用简单的、不容易出错的代码去测试复杂的、容易出错的代码。
测试代码只在开发和调试时有用,如果正式发布(Release)的软件也要运行这些测试代码就会严
重影响性能了,如果在包含assert.h之前定义一个NDEBUG 宏(表示No Debug),就可以禁
用assert.h 中的assert 宏定义,这样代码中的所有assert 测试都不起作用了:
#define NDEBUG
#include <stdio.h>
#include <assert.h>
...
注意NDEBUG 和我们以前使用的宏定义有点不同,例如#define N 20将N 定义为20,在预处理时把代
码中所有的标识符N 替换成20,而#define NDEBUG把NDEBUG 定义为空,在预处理时把代码中所有的
标识符NDEBUG 替换成空。这样的宏定义主要是为了用#ifdef 等预处理指示测试它定义过没有,而不
是为了做替换,所以定义成什么值都无所谓,一般定义成空就足够了。
还有另一种办法,不必修改源文件,在编译命令行加上选项-DNDEBUG 就相当于在源文件开头定义
了NDEBUG 宏。宏定义和预处理到第 21 章预处理再详细解释,在第 4 节 “ 其它预处理特性” 将给
出assert.h 一种实现。