在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
我的问题是
- 上述程序可以进一步优化(速度)而不会丢失任何错误检查条件吗?
- 是否有其他最佳优化(速度)方法可以解决同样的问题?
醇>
答案 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>