如何正确的写加减乘除函数

有符号数加减法,存在溢出问题,经常被用于攻击构造。那么如何编写一个可以防止溢出的“正确的”函数呢?

加法运算

错误写法

signed int func(signed int a, signed int b) {
	return a + b;
}

分析:a与b相加可能溢出(又分为上溢和下溢)
上溢条件:a + b > INT_MAX
下溢条件:a + b < INT_MIN
正确写法

signed int func(signed int a, signed int b)
{
	if (((b > 0) && (a > INT_MAX - b)) || 
		((b < 0) && (a < INT_MIN - b))) {
		/* Handle error */
	} else {
		return a + b;
	}
}

减法运算

错误写法

signed int func(signed int a, signed int b)
{
	return a - b;
}

正确写法

signed int func(signed int a, signed int b)
{
	if ((b > 0 && a < INT_MIN + b) ||
		(b < 0 && a > INT_MAX + b)) {
		/* Handle error */
	} else {
		return a - b;
	}
}

乘法

错误写法

signed int func(signed int a, signed int b)
{
	return a * b;
}

正确写法

#include <stddef.h>
#include <assert.h>
#include <limits.h>
#include <inttypes.h>

extern size_t popcount(uintmax_t)
#define PRECISION(umax_value) popcount(umax_value)


signed int func(signed int a, signed int b)
{
	signed long long tmp;
	assert(PRECISION(ULLONG_MAX) >= 2 * PRECISION(UINT_MAX));
	tmp = (signed long long)a * (signed long long)b;
	
	if ((tmp > INT_MAX) || (tmp < INT_MIN)) {
		/* 处理错误 */
	} else {
		return tmp;
	}
}

除法

错误写法

signed int func(signed int a, signed int b)
{
	if (b == 0) {
		/* 处理错误 */
	} else {
		return a / b;
	}
}

正确写法

signed long func(signed long a, signed long b)
{
	if ((b == 0) || ((a == LONG_MIN) && (b == -1))) {
		/* 处理错误 */
	} else {
		return a / b;
	}
}

这里这个(a == LONG_MIN) && (b == -1)的条件非常有意思,要理解为什么这种情况下计算会出错,得理解数在计算机中是如何存储的。

取余

错误写法

signed long func(signed long a, signed long b)
{
	if (b == 0) {
		/* 错误处理 */
	} else {
		return a % b;
	}
}

正确写法

#include <limits.h>

signed long func(signed long a, signed long b)
{
	if ((b == 0) || ((a == LONG_MIN) && (b == -1))) {
		/* 错误处理 */
	} else {
		return a % b;
	}
}

版权声明:本文为velscode原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。