用于检查整数是否位于给定范围/边界检查中的优化方法

时间:2015-01-20 06:21:01

标签: c linux

在c编程中查找是否有任何给定值位于范围之间,if条件如下所示。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define U_LIMIT     100
#define L_LIMIT    -100

int check_num(char *in)
{
    int i = 0;

    if (in[i] == '-' || in[i] == '+') {
        i++;
        if (in[i] == '\0')
            return 0;
    }

    while (i < strlen(in)) {
        if(!isdigit(in[i]))
            return 0;
        i++;
    }
    return 1;
}

int main(int argc, char *argv[])
{
    int i;
    if (argc != 2)
       return 1;

    if (!check_num(argv[1])) {
       printf("Not a digit\n");
       return 1;
    }

    i = atoi(argv[1]);
    if (i < U_LIMIT && i > L_LIMIT)
       printf("Within Range\n");
    else
       printf("Outside Range\n");

    return 0;
}

示例输出:

./a.out 1    -> Within Range
./a.out 100  -> Outside Range
./a.out -100 -> Outside Range
./a.out -    -> Not a digit
./a.out e    -> Not a digit

我的问题是

  
      
  1. 上述程序可以进一步优化(速度)而不会丢失任何错误检查条件吗?
  2.   
  3. 是否有其他最佳优化(速度)方法可以解决同样的问题?
  4.   

3 个答案:

答案 0 :(得分:2)

while (i < strlen(in))

由于您的问题询问时间优化,因此这是您改进的一部分

int n =strlen(in);

while(i<n){

这样做strlen()不会在每次迭代中计算,这是耗时的。其余代码应该没问题,因为您使用if()来检查更简单和最好的边界。

答案 1 :(得分:2)

无需检查用户输入,然后解析它。只需使用sscanf即可完成两项任务:

#include <stdio.h>

#define U_LIMIT     100
#define L_LIMIT    -100

int main(int argc, char *argv[])
{
    int i;

    if (argc != 2) {
        printf("Missing argument\n");
        return 1;
    }

    /* ALWAYS check the return value from scanf() and friends!
     * It returns the number of items in your format string successfully
     * assigned to. Make sure it matches the number of values you're
     * expecting, (one in this case).
     */
    if (sscanf(argv[1], "%d", &i) != 1) {
        printf("Invalid number\n");
        return 1;
    }

    /* Doesn't get any simpler than an if statement */
    if (i < U_LIMIT && i > L_LIMIT)
       printf("Within Range\n");
    else
       printf("Outside Range\n");

    return 0;
}

此外,不要在游戏初期尝试疯狂优化。专注于编写干净,简洁的代码,让优化编译器为您完成剩下的工作。

-O3优化级别,GCC为main()生成以下程序集。我只保留了有关整数比较的有趣部分。

GCC命令行:gcc -Wall -Werror -O3 -g test.c

Objdump命令行:objdump -d -Mintel -S a.out

if (i < U_LIMIT && i > L_LIMIT)
  4004e5:   mov    eax,DWORD PTR [rsp]
  4004e8:   add    eax,0x63
  4004eb:   cmp    eax,0xc6
  4004f0:   jbe    400500 <main+0x50>

注意编译器为您做了什么。它首先将0x63(99)添加到i,然后将该值与0xC6(198)进行比较,而不是进行两次比较。这相当于:

if ((unsigned int)(i + 99) <= 198)

这样,只需要一个条件分支。很酷的部分是它做了无符号比较。如果i小于-100,那么(i + 99)仍然是负数,并且解释为无符号整数是0xfffffed3(一个非常大的数字),&lt; = 198。

最好的部分?你甚至不需要考虑它!

答案 2 :(得分:1)

选项strtol()

您可能只想使用strtol(3)

long int strtol(const char *nptr, char **endptr, int base);

The strtol() function converts the initial part of the string in 
nptr to a long integer value according to the given base, which 
must be between 2 and 36 inclusive, or be the special value 0.

此外:如果输入数据溢出或下溢,您将获得返回值。

此外:该功能会将endptr置于第一个&#39;无效的&#39;字符。

char*     in = "123a";
size_t   len = strlen(in);
char*    end = NULL;
long int out = strtol(in, &end, 10);

if (out == LONG_MIN || out == LONG_MAX) {
    // underflow || overflow
} else if (in + len < end) {
    // invalid chars while parsing
} else {
    // ok
}

选项sscanf()

标准的替代方法是使用sscanf(),这通常是正常的,但是当你需要解析很多整数时可能会成为瓶颈(重新解析格式字符串是一个问题)。

选项rollyourown()

既然您已经自己进行了检查,那么您也可以自己进行解析(取自qmail的scan_ulong.c):

unsigned int scan_ulong(const char *s, unsigned long *u) {
    unsigned int pos = 0;
    unsigned long result = 0;
    unsigned long c;
    while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
        result = result * 10 + c;
        ++pos;
    }

    if (u) {
        *u = result;
    }
    return pos;
}

此部分相当于您的isdigit()代码,但同时也会解析该数字。如果解析数字是项目的速度问题,请考虑选项strtol()(取决于您使用的编译器/平台)或scan_ulong()变体,否则只需坚持使用sscanf()。< / p>