房地产网站建设联系方式,网页版传奇源码,农家院做宣传应该在哪个网站,手机助手app下载在C语言程序开发中#xff0c;程序员写代码时应该考虑的“面面俱到”#xff0c;这样才能写出功能稳定的程序。例如#xff0c;在实现 open() 函数时#xff0c;先完成它的功能固然是重要的#xff0c;但是程序员还需要考虑各种“意外”#xff0c;比如下面这种情况。假设…在C语言程序开发中程序员写代码时应该考虑的“面面俱到”这样才能写出功能稳定的程序。例如在实现 open() 函数时先完成它的功能固然是重要的但是程序员还需要考虑各种“意外”比如下面这种情况。假设不存在 /dev/sth 这个文件仍然调用 open() 函数打开它int fd open(/dev/sth, O_RDONLY);此时 open() 函数不应该感到迷惑而是具备处理这种“意外”的能力。标准库的 open() 函数在遇到这种情况时会返回一个错误码对应着“文件不存在”的错误信息。所以我们在开发C语言程序的过程中写出的代码也应具备这种处理“意外”的能力。处理“意外”最常用的方式之一就是返回一个错误码输出一段错误提示信息这一点其实之前的文章讨论过。使用 assert在C语言程序开发阶段为了方便我们可以在可能出现不预期的“意外”处使用 assert()。assert() 的C语言原型如下#include void assert(scalar expression);使用它需要包含 assert.hassert() 接收一个参数 expression可以是一个表达式如果 expression 为真则什么都不会发生。如果 expression 为假则 assert() 会终止C语言程序并且输出 assert 失败的代码位置。例如下面这段C语言代码int fd open(/dev/sth, O_RDONLY);assert(fd 0);printf(fd %d\n, fd);编译并执行得到如下结果# gcc t.c# ./a.out a.out: t.c:11: main: Assertion fd 0 failed.Aborted可以看出第 12 行的 printf() 函数并没有被执行。这是因为程序运行环境里并没有 “/dev/sth” 这个文件所以 open() 函数执行失败传递给 assert() 的参数为假C语言程序被终止并且输出 t.c 源文件第 11 行代码 assert 失败。assert() 可以输出出错的代码位置这个特性在较为大型的C语言程序开发中是非常好用的因为无需程序员再去手工调试代码排查出错代码的位置了。不过assert() 在遇到假参数时直接将C语言程序终止太过于死板。比如某个C语言程序有两套逻辑第一套逻辑在 open() 函数成功打开文件时运行第二套逻辑则在 open() 函数打开文件失败时运行。要是使用 assert() 判断 open() 函数是否成功打开文件则第二套逻辑永远没有机会运行。所以assert() 一般仅用于开发阶段帮助程序员定位错误不能依赖 assert() 处理“意外”。事实上为了便于使用在定义了 NDEBUG 宏之后assert() 就不再生成代码了此时 assert() 相当于一个空格。请看下面这段C语言代码#include #include #include #include #define NDEBUG#include int main(){int fd open(/dev/sth, O_RDONLY); assert(fd 0);printf(fd %d\n, fd);return0; }编译上述C语言代码并执行得到如下输出# gcc t.c# ./a.out fd -1编译时 assert可以看出assert() 用于处理C语言程序可能出现诸多预期之外的“意外”时很有用它能够自己输出究竟哪一个“意外”发生。但是 assert() 也是死板的它在遇到假条件时直接把程序终止剩余的代码逻辑不再有机会执行。另外还有一点要说明assert() 本身也会影响C语言程序的运行效率这也是它常常只被使用在开发阶段的另一个原因。其实仔细想想使用 assert() 的目的其实只是希望它能够在C语言程序遇到不预期的“意外”时提醒程序员我们并不关心 assert() 是否参与程序运行。如果使用 assert() 判断的是常量表达式那我们可以自己定义一个 static_assert() 宏并且让它在编译时就判断条件表达式是否成立这样的宏可能在某些场合更加好用。那该如何实现编译时 assert 这个功能呢其实很简单首先应该明白数组的长度不可能是负数基于这一点static_assert() 宏就容易实现了请看下面的C语言代码#define static_assert(expr) \do{ char tmp[(expr)?1:-1]; }while(0)如果条件表达式为真则 static_assert() 宏会定义一个长度为 1 的数组否则就会尝试定一个长度为 -1 的数组此时必定无法编译通过。这里值得一提的一个小技巧是使用 {} 符号将定义的 tmp 数组的作用域限定在本次调用的 static_assert 宏里避免多次调用 static_assert 时出现重复定义。写出如下C语言代码测试之int main() { static_assert(21);printf(assert 21\n);static_assert(21);printf(assert 21\n);return0; }编译这段C语言代码得到如下输出显然static_assert() 宏在编译阶段就将假条件表达式找出来了。可能有些读者会觉得如果 assert 成功就会定义一个 tmp 数组虽然它的长度很短但是仍然浪费了栈空间。其实这里可以把长度为零的数组即#define static_assert(expr) \do{ char tmp[(expr)?0:-1]; }while(0)在 assert 成功时会执行 char tmp[0];它的长度为 0感兴趣的读者可以使用 sizeof() 测试一下。到这里我们就较为粗略的定义好了 static_assert 宏它在编译阶段就能发现假条件。小结本节主要介绍了 assert() 的使用应该能够发现在开发阶段它能够帮助程序员快速的定位“意外”也讨论了 assert() 的不足之处并在此基础上自己定义了“编译时”的static_assert 宏。按照这样的思路其实还有很多定义 static_assert() 宏的其他方法具体哪些方法留给读者自己思考了。