防御性编程

防御性编程（Defensive programming）是防御式设计的一种具体体现，它是为了保证，对程序的不可预见的使用，不会造成程序功能上的损坏。它可以被看作是为了减少或消除墨菲定律效力的想法。防御式编程主要用于可能被滥用，恶作剧或无意地造成灾难性影响的程序上。

主要概念:若常式收到錯誤的資料，即便錯誤資料是由其他常式出錯所造成，此常式也不會受到傷害。

防御性编程通常通过以下途径，从而提高软件和源码的质量：

 提高工程质量——减少bug和问题
 提高源码可读性—— 源码应该变得可读且可理解，并且能经受代码审计。
 让软件能通过预期的行为来处理不可预期的用户操作。

值得注意的是，过度的防御性编程可能会预防不可能会发生的错误，这样将导致运行时间与维护的损耗。当源码中拥有过多异常捕捉和异常处理，这有可能导致结果不正确或者被隐藏。

安全编程 
防御性编程有时也被计算机科学家称为安全编程（Secure programming）。潜在的软件缺陷可能会被黑客利用，而进行代码注入，拒绝服务攻击或其他攻击。

防御性编程与非防御性编程之间的区别在于，程序员不会对特定的函数调用或库的使用情况做假设。下面是一个例子：

int risky_programming(char *input){
  char str[1000+1];     // 1000为内容，1为空终止字符串
  // ...
  strcpy(str, input);   // 复制输入
  // ...
}

当输入超过1000个字符时，该函数将会崩溃。一些新手程序员可能并不会觉得这是个问题，因为没有用户会输入这么长的字符串。实行防御性编程的程序员不会允许这样的错误，因为这段程序包含一个已知的bug，一个可能会导致缓冲区溢出攻击的漏洞。下面这个例子是一个解决方案：

int secure_programming(char *input){
   char str[1000+1];  // 1000为内容，1为空终止字符串
   
  // ...
  
  // 限制越界拷贝。
  strncpy(str, input, sizeof(str)); 
 
  // 当最后一个字符不为空终止字符串，我们可以选择只处理未越界的内容，或者直接停止程序
  str[sizeof(str) - 1] = '\0';
  // ...
}

进攻式编程 
进攻式编程（Offensive programming ）是防御式编程（ defensive programming）的子集，其思想为：在重点的代码处不应该一些错误进行处理。在实践中，只有来自于软件外部的错误应该被处理（如：用户的输入）；理论上，软件自身和相关可控的数据是可信的。

可信内部数据的校验

过度防御式编程 
const char* trafficlight_colorname(enum trafficlight_color c) {
    switch (c) {
        case TRAFFICLIGHT_RED:    return "red";
        case TRAFFICLIGHT_YELLOW: return "yellow";
        case TRAFFICLIGHT_GREEN:  return "green";
    }
    return "black"; //万一发生其他输入值时，使用信号灯将不亮
}

进攻式编程 
const char* trafficlight_colorname(enum trafficlight_color c) {
    switch (c) {
        case TRAFFICLIGHT_RED:    return "red";
        case TRAFFICLIGHT_YELLOW: return "yellow";
        case TRAFFICLIGHT_GREEN:  return "green";
    }
    assert(0); // 认为这种情况不可能发生
}

可信的软件组件

过度防御式编程 
if (is_legacy_compatible(user_config)) {
    // 策略：新代码可能不完全兼容“用户配置”
    old_code(user_config);
} else {
    // 策略：新代码操作“用户配置”，可能出会错
    // 一旦出错时，，使用旧代码处理
    if (new_code(user_config) != OK) {
        old_code(user_config);
    }
}

进攻式编程 
// 完全信任新代码
new_code(user_config);

技术层面 
下面是常见的防御式编程的技术。

理性地重用代码 
如果源码是广为利用地且经得起测试的，那么重用代码将有助于减少bug。

然而，重用代码往往不是最好的方案，越是底层的代码（往往指框架），重用代码带来的潜在危害就越是大。重用过于复杂的代码将大大地增加代码的复杂度。

遗留代码 
在重用旧代码、包、API、配置等事物前，我们必须思考这些事物是否值得被重用，思考这些是否会造成遗留问题。

遗留问题的产生，往往是因为旧的设计无法适应当今的需求，尤其旧的设计往往不是为了当今需求，因此开发和测试可能存在缺陷。

参见 
 计算机安全
 健壯性 (计算机科学)

参考文献 

编程原则
編程典範