c语言算术运算符越界问题解决方案

大量的安全漏洞是由于计算机算术运算的微妙细节引起的, 具体的C语言, 诸如符号数和无符号数之间转换, 算术运算的越界都会导致不可预知的错误和安全漏洞, 具体的案例数不胜数.

作为一个系统程序员, 有必要对这些细节有深入的了解. 本篇参考csapp, 主要介绍如何判断算术运算的越界问题.

(虽然本篇的代码经过大量的测试, 但本人仍然无法保证代码的正确性, 希望大家纠错).
讲解的原则是"摆定理, 不证明, 写代码". 具体的证明过程在csapp中有详细的讲解, 也不是太难. 主要使用关键定理来写代码. Go~
问题一: 无符号数的加法越界问题
[定理]


[理解]
这个定理比较容易, 也比较能让人接受. 不解释啦.


代码如下:

/* Determine whether arguments can be added without overflow */
int uadd_ok(unsigned int x, unsigned int y)
{
return !(x+y < x);
}

问题二: 无符号数的减法越界问题
[定理]

[理解]
1. 计算机中没有减法, x-y = x+(-y), 这里的-y就是上述的y的加法逆元. 不管是有符号还是无符号, 都是转换为加法运算. 只是加法逆元的定义不同.

3. C语言保证 -x = ~x+1; 可以验证这种方式与上面公式等价.
4. s=x-y = x+(-y). 那么 不会溢出 等价于 y不为0 或者 !uadd_ok(x, -y).




代码如下:

/* Determine whether argumnts can be substracted without overflow */
int usub_ok(unsigned int x, unsigned int y)
{
return !y || !uadd_ok(x, -y);
}

问题三: 无符号数的乘法越界问题
[定理]


[理解]
等价条件可以相互推导即可.


代码如下:

/* Determine whether arguments can be multiplied without overflow */
int umul_ok(unsigned int x, unsigned int y)
{
unsigned int p = x * y;
return !x || p/x==y;
}

问题四: 有符号数的加法越界问题
[定理]
对于两个有符号数x, y. 越界的等价条件是x,y为负数, x+y为正数或者x,y为正数, x+y为负数.
[理解]
这个定理比较容易.


代码如下:

/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y)
{
return !(x<0&&y<0&&x+y>0 || x>0&&y>0&&x+y<0);
}

问题五: 有符号数的减法越界问题
[定理]


[理解]
同无符号的减法一样, 只是加法逆元的定义不同, 但是位模式是一样的. C语言可以保证-x=~x+1. 同样也分两种情况讨论.见代码.

代码如下:

/* Determine whether arguments can be subtracted without overflow */
int tsub_ok(int x, int y)
{
  #if 0
  if (y == INT_MIN)
  return x<0;
  else
  return tadd_ok(x, -y);
  #endif
  return y==INT_MIN&&x<0 || y!=INT_MIN&&tadd_ok(x, -y);
}

问题六: 有符号数的乘法越界问题
[定理]
完全同无符号的乘法一样.


代码如下:

/* Determine whether arguments can be multiplied without overflow. */
int tmul_ok(int x, int y)
{
#if 0
int p = x * y;
return !x || p/x==y;
#endif
return umul_ok(x, y); /* 直接调用 */
}

(0)

相关推荐

  • C语言逻辑运算符知识整理

    在高中数学中,我们学过逻辑运算,例如p为真命题,q就假命题,那么"p且q"为假,"p或q"为真,"非q"为真. 在C语言中,也有类似的逻辑运算: 运算符 说明 结合性 举例 && 与运算,双目,对应数学中的"且" 左结合 1&&0.(9>3)&&(b>a) || 或运算,双目 左结合 1||0.(9>3)||(b>a) ! 非运算.单目 右结合 !a.!(

  • C语言运算符优先级列表(超详细)

    每当想找哪个运算符优先级高时,很多时候总是想找的就没有,真让人气愤!现在,终于有个我个人觉得非常全的,分享给大家,欢迎拍砖! C语言运算符优先级 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成员选择(对象) 对象.成员名 -- -> 成员选择(指针) 对象指针->成员名 -- 2 - 负号运算符 -表达式 右到左 单目运算符 ~ 按位取反运算符 ~表达式 ++ 自增运算符 +

  • C语言 解决不用+、-、×、÷数字运算符做加法的实现方法

    题目:写一个函数,求两个整数的之和,要求在函数体内不得使用+.-.×.÷. 分析:这又是一道考察发散思维的很有意思的题目.当我们习以为常的东西被限制使用的时候,如何突破常规去思考,就是解决这个问题的关键所在. 看到的这个题目,我的第一反应是傻眼了,四则运算都不能用,那还能用什么啊?可是问题总是要解决的,只能打开思路去思考各种可能性.首先我们可以分析人们是如何做十进制的加法的,比如是如何得出5+17=22这个结果的.实际上,我们可以分成三步的:第一步只做各位相加不进位,此时相加的结果是12(个位数

  • C语言中6组指针和自增运算符结合方式的运算顺序问题

    在C语言中,当指针运算符和++或者–结合时很容易分不清运算顺序,在这里总结一下,下面一共分析6中组合: * p++,(* p)++,* (p++),++* p,++( * p), * (++p). 先看段代码以及输出: #include<stdio.h> int main() { int a[3]={1,3,5}; int *p=a; printf("----------------1----------------\n"); printf("%d\n"

  • C语言 运算符详细介绍及示例代码

    C 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号.C 语言内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 本章将逐一介绍算术运算符.关系运算符.逻辑运算符.位运算符.赋值运算符和其他运算符. 算术运算符 下表显示了 C 语言支持的所有算术运算符.假设变量 A 的值为 10,变量 B 的值为 20,则: 运算符 描述 实例 + 把两个操作数相加 A + B 将得到 30 - 从第一个操作数中减去第二个操作数 A

  • C语言位运算符:与、或、异或、取反、左移与右移详细介绍

    位运算是指按二进制进行的运算.在系统软件中,常常需要处理二进制位的问题.C语言提供了6个位操作运算符.这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型. C语言提供的位运算符列表:运算符 含义 描述& 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0| 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1~ 取反 ~是一元运算符,用来对一个二进制数按位取反,即将

  • C语言运算符及其优先级汇总表口诀

    C语言运算符及其优先级汇总表口诀 圆下箭头一顿号非凡增减富强针地长三乘除,四加减,五移位千万别把鱼忘记,它在盛饭的厨子里小灯大灯灯灯不等爸喂鱼,舅疑惑,裸鸡也疑惑十三姨,十四父,十五逗,兜到低 "圆下箭头一顿号"指的是第15级的运算符.其中圆指的是运算符(),下指的是下标运算符[],箭头指的是指向结构体成员运算符->,顿号指的是结构体成员运算符. "非凡增减富强针地长"指的是第14级的运算符.其中非指的是逻辑运算符!,凡指的是按位取反运算符~,增减指的是自增和

  • C语言中逻辑运算符与条件运算符的学习教程

    逻辑运算符 逻辑运算符,用于对包含关系运算符的表达式进行组合,形成新的表达式:结果也是只有真或假两种情况,结果值用 BOOL 类型变量存储. 运算符 解释 结合方式 () [] -> . 括号(函数等),数组,两种结构成员访问 由左向右 ! ~ ++ -- + - * & (类型) sizeof 否定,按位否定,增量,减量,正负号, 间接,取地址,类型转换,求大小 由右向左 * / % 乘,除,取模 由左向右 + - 加,减 由左向右 << >> 左移,右移 由左向右

  • 简单总结C语言中的运算符优先级

    C语言中有很多运算符,除了四则运算,还有位运算.比较运算.逻辑运算.赋值运算等等,令人眼花缭乱的同时,优先级也让人头疼.通常建议在写代码的时候不用省略括号,但是并不是所有程序员都会按照这个规矩来,因此还是有必要记录一下,以备查阅. 总结几个比较重要的原则: 取成员的"0"目运算符[() . -> []]优先级最高: 单目运算符高于双目运算符: 四则运算高于移位运算: 移位运算高于比较运算: 比较运算高于位运算,位运算高于逻辑运算: 逻辑运算高于赋值运算: 逗号永远最低.

  • C语言运算符的优先级和结合性实例详解

    运算符是告诉编译程序执行特定算术或逻辑操作的符号.C语言的运算范围很宽,把除了控制语句和输入输出以外的几乎所有的基本操作都作为运算符处理.主要分为三大类:算术运算符. 关系运算符与逻辑运算符.除此之外,还有一些用于完成特殊任务的运算符. 先来看一个例子: #include <stdio.h> int main(){ int a=10,b=1,c=2; a=b=c; printf( "12+3*5=%d\n", 12+3*5); printf( "a=%d, c=%

随机推荐