使用内嵌汇编检查溢出

在面试中经常考到,实际项目中也经常需要面临这样一个问题,即如何判断一个整数操作是否发生了溢出。C#语言提供了`checked`关键字用于处理这一问题,在C语言中我们可以使用内嵌汇编语句来检查溢出。

In computer processors, the overflow flag (sometimes called V flag) is usually a single bit in a system status register used to indicate when an arithmetic overflow has occurred in an operation, indicating that the signed two’s-complement result would not fit in the number of bits used for the operation (the ALU width).

C语言中不能够直接访问寄存器,但是可以通过内嵌汇编语言,来访问寄存器的值。不过这样做有一点缺陷是,汇编代码与计算机体系结构相关,在可移植性上需要更多考虑。

经过查资料,在X86体系结构中,可以通过汇编指令`JO`和`JNO`来实现基于Overflow flag的条件跳转。JO (Jump on Overflow)意味着有溢出时跳转,JNO (Jump on Not Overflow)则反之。下面通过一个小例子来解释如何进行溢出检查。

输入:一个符合`[+|-]?\n+`正则表达式的字符串。

输出:输入的字符串是否可以转换为一个`int`型常量而不发生溢出。

bool TryParseInt(const char *str, int *pn)
{
    bool isAddOp = true;
    if (*str == '+') {
        str++;
    } else if (*str == '-') {
        isAddOp = false;
        str++;
    }

    int n = 0;
    while (*str != '\0') {
        n *= 10;
        __asm__ goto ("jo %l0"
            : /* no output */
            : /* no input */
            : /* no clobber */
            : PARSE_INT_FAILD
        );
        if (isAddOp) {
            n += *str - '0';
        } else {
            n -= *str - '0';
        }
        __asm__ goto ("jo %l0"
            : /* no output */
            : /* no input */
            : /* no clobber */
            : PARSE_INT_FAILD
        );
        str++;
    }
    *pn = n;
    return true;

PARSE_INT_FAILD:
    return false;
}

失效情况

有时候编译器会使用`LEA`指令进行计算上的优化,由于`LEA`指令不会设置任何标志位,因此不能够使用`JO`来检测到运算溢出。