assert

义项:编程术语
assert

assert是命令式编程语言中的一类语句或宏,用于在程序执行到某一位置时检查一个布尔表达式是否成立,不成立即表示程序存在缺陷。它常用于描述开发者对代码状态的前置条件、后置条件与不变式,并在开发与调试阶段暴露逻辑错误。多数语言允许在发布版本中关闭断言检查,因此断言并不承担运行期错误处理职能。[1][2]

目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

assert是写在程序某一点上的断言语句,其参数是一个布尔表达式,含义是程序运行到该处时该表达式应当恒为真。常见的写法是在语句后接一个条件,例如在 C 语言中写作 assert(x > 0),位于其后的代码即可依赖这一条件成立。[1]在 C 语言的实现中,assert 由标准头文件 assert.h 定义为宏;C++ 的 assert.h 与 cassert 也提供同名宏。Java、Python 等语言则把 assert 作为语言层面的关键字或语句。[3][4]更一般地说,断言是描述程序运行中某个状态性质的逻辑公式,被置于程序文本的特定位置,作为程序与其规格之间的桥梁。[5]

原理

以 C 语言为例,assert 宏展开为对一个标量表达式的检查:表达式为真时不起作用,为假时把出错的实参文本、源文件名、行号以及所在函数名输出到标准错误流,随后调用 abort() 终止程序。[2]glibc 的实现中,未定义 NDEBUG 时 assert 展开为三元表达式,仅在条件为假时调用内部函数报告位置信息;定义了 NDEBUG 后该宏被定义为空操作,不产生任何代码。这种开关设计使断言可以在编译期被整体移除。[6]Java 的 assert 语句在条件为假时抛出 AssertionError,并支持 assert 表达式1 : 表达式2 的形式,把表达式2 的值作为详细信息附在错误中;断言默认关闭,需要运行时启用,关闭时该语句在语义上等同于空语句。[7]Python 的 assert 语句等价于在 debug 为真时判断条件,条件为假则抛出 AssertionError;以优化选项启动解释器时,代码生成器不会为 assert 生成任何字节码。[8]断言的检查时机不限于运行期。编译期检查的断言称为静态断言,C11 与 C++11 通过 static_assert 直接支持:其常量表达式在编译时求值,为非零时不产生代码且无运行开销,为零时编译失败并输出指定的字符串信息。[9][10]

发展历程

把逻辑断言写入程序以论证正确性的思想可以追溯到 20 世纪 40 年代末:冯·诺伊曼与戈尔茨坦 1947 年的论文、图灵 1949 年的《检查一个大型程序》都引入了类似的思路,但当时并未进入计算机科学的公共视野。1967 年,罗伯特·弗洛伊德在《给程序赋予含义》中系统提出归纳断言方法;1969 年,托尼·霍尔(C. A. R. Hoare)在《计算机程序的公理基础》一文中将其形式化,提出后来被称为霍尔三元组({p} S {q})的系统,用前断言和后断言刻画语句执行的输入输出行为。[11]中文文献对这段脉络的记述与此一致:弗洛伊德于 1967 年提出归纳断言方法,霍尔于 1969 年将其形式化并提出程序验证的公理系统,此后陆续出现基于归纳断言的自动与半自动程序验证系统。[12]1976 年,戴维·马图谢克在《断言语句之理由》一文中主张把 assert <布尔表达式> 作为一条简单、透明的语句引入类 ALGOL 语言,并列举了它记录假设、表达不变式、可在不影响程序行为的前提下增删、可通过编译选项按注释处理等优点,这为断言从验证理论走入工程实践提供了论证。[13]此后,断言逐步成为语言与库的内置设施:C 通过 assert.h 提供宏,Java 在 1.4 版本引入 assert 语句,Python 将其作为语句提供,C11 与 C++11 标准化了 static_assert,C23 与 C++17 起该关键字允许省略第二个字符串实参。[3][4][14]

应用

断言在开发周期中主要用于暴露逻辑错误:程序员在假定必然成立的条件下放置断言,一旦失败即立刻得到失败位置与现场信息,便于定位问题,许多实现还会随即终止程序以免错误状态扩散。[3]按断言所处位置,可分为前置条件、后置条件与类不变式等类别:前置条件描述调用前的合法状态,后置条件描述成功完成后应满足的状态,类不变式描述每个实例都应保持的性质,因而断言可支撑一种非正式的契约式设计风格。[4]在循环中,断言还用于书写循环不变式,即每次迭代前后都成立的命题,用以论证循环能达到预期结果。[15]静态断言常见于模板元编程与底层语言中,用于在编译阶段校验类型大小、结构体字段偏移、枚举长度等常量性质,例如以 static_assert(offsetof(struct timer, DATA) == 4, ...) 的形式固定数据布局。[9][3]断言也被用于测试驱动开发与规范检验:断言构造使语言本身即可承载这类检查,无需引入第三方库。[3]静态分析工具亦会建模 assert 的行为,把失败的断言视为运行期错误,从而在程序执行前发现可能触发断言失败的路径。[16]

局限

断言并非运行期错误处理手段。C 语言的安全编码规范指出,assert 应只用于防范程序员自身假设出错,不应用于检查非法用户输入、文件打开失败、网络错误、内存耗尽、系统资源耗尽、系统调用失败或权限不足等运行期状况;这些情况需要在发布版本中依然生效的处理逻辑,而断言通常在上线前即被关闭。[2]Java 的文档同样规定,断言不应用于公有方法的参数检查,也不应用于执行程序正常运作所必需的副作用,因为断言可能被禁用,公有方法无论断言是否开启都必须校验参数并抛出约定类型的异常。[7]断言表达式中的副作用会带来风险:若表达式带副作用的求值在关闭断言后不再发生,程序行为就会随断言开关而改变,因此标准断言中的表达式不应产生副作用。Java 在早期的 Dalvik 虚拟机上默认忽略 assert,需要通过系统属性或命令行参数显式开启。在服务器程序与嵌入式系统的部署环境中,断言失败可能被外部输入触发,从而造成服务拒绝,此时写入日志并拒绝请求之类的软失败方式更为合适。[2]此外,断言的存在与否并不等价于代码正确:缺少断言并不说明程序有错,断言只是发现并消除缺陷的辅助诊断工具。[2]断言还有其表达能力边界:Java 的 assert 构造并非完整的契约式设计设施,只提供有限的布尔检查;把断言用于承诺编译器某条边界条件不可达时,关闭断言反而可能降低性能。[4][3]

参见

  • 霍尔逻辑 —— 用前断言与后断言推理程序正确性的形式系统,是断言语义的理论基础。

  • 契约式设计 —— 以先决条件、后置条件与不变式规范模块接口的设计方法。

  • 循环不变式 —— 在循环每次迭代前后都应成立的断言,用于论证循环正确性。

  • 单元测试 —— 以用例检查程序行为是否符合预期的测试方式。

  • 静态分析 —— 在不运行程序的前提下检查代码性质的技术,可实现编译期断言。

参考资料

  1. epfl.ch 上的网页 [引用日期2026-10-29]
  2. viewpage [引用日期2026-10-29]
  3. Assertion (software development) [引用日期2026-10-29]
  4. Programming With Assertions [引用日期2026-10-29]
  5. Assertions: A Personal Perspective [引用日期2026-10-29]
  6. C and C++ linkage orwhat does extern "C" means? [引用日期2026-10-29]
  7. アサーションを使用したプログラミング [引用日期2026-10-29]
  8. python.org 上的网页 [引用日期2026-10-29]
  9. viewpage [引用日期2026-10-29]
  10. static-assert-c [引用日期2026-10-29]
  11. references [引用日期2026-10-29]
  12. [科普中国]-程序验证 - 版权归原作者所有,如有侵权,请联系我们 [引用日期2026-10-29]
  13. JavaScript disabled - S I G P L A N Notices 36 1976 August [引用日期2026-10-29]
  14. assert.h (GNU Gnulib) [引用日期2026-10-29]
  15. CS 2110 Code Style Guidelines [引用日期2026-10-29]
  16. User assertion - assert statement fails [引用日期2026-10-29]
词条评价
词条统计

浏览次数:0 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-28T18:44:14Z

历史版本

本条目引用的词条
霍尔逻辑 契约式设计 循环不变式 单元测试 静态分析