如何安全快速地从整数中提取数字?

3
我们目前有一些代码可以从int中提取数字,但我需要将其转换到一个没有snprintf的平台,并且我担心会发生缓冲区溢出。我已经开始编写自己的可移植(并优化的)snprintf,但我被告知在这里询问,以防有更好的想法。
int extract_op(int instruction)
{ 
    char buffer[OP_LEN+1];
    snprintf(buffer, sizeof(buffer), "%0*u", OP_LEN, instruction);
    return (buffer[1] - 48) * 10 + buffer[0] - 48;
}

我们使用C字符串是因为速度非常重要。


4
你可以使用字符'0'代替数字48作为常量:return (buffer[1] - '0') * 10 + buffer[0] - '0';这样做并不能解决你的问题,但是这样会更加清晰明了。 - zneak
2
似乎你可以通过除以10(而不是使用snprintf)来实现这个。我发现奇怪的是,buffer[1] 显然比 buffer[0] 更重要。 - Matthew Flaschen
2
@Billy,那是误导。任何正确编写的C代码都只会计算一次长度,并根据需要重复使用它。 - R.. GitHub STOP HELPING ICE
@R.. :是的,那是真的。然而,根据那个定义,我所看到的大多数 C 代码都没有“适当地编写”。(公平地说,我看到的大多数 C 代码来自学生) - Billy ONeal
@LokiAstari 它在x86上非常快,用C/C++编写 - Ariel Bold
显示剩余5条评论
5个回答

7
您不需要将指令转换为字符数组,只需要保留"前两位数字",如下所示:
int extract_op(unsigned int instruction)
{
    int first = 0;
    int second = 0;
    while(instruction) {
        second = first;
        first = instruction % 10;
        instruction /= 10;
    }
    return first + 10 * second;
}

我认为return语句中的表达式是错误的,但它确实模拟了你所做的事情:十倍于第二位数字之和再加上第一位。

我怀疑在你特定的平台和编译器上,速度可能会比现在更快,当然这需要你自己去测量。


3
抱歉,为什么你要从本地人名中去掉所有的元音字母?(另外,请检查你的循环,目前你使用的是 first 而不是 frst。) - zneak
@zneak,没有真正的原因,让我编辑一下,使它再次变得有意义;-)。 - Alex Martelli
@Ari 你也可以通过使用 divmod 或等价函数来计算并更新你的值来获得改进。 - Anycorn
2
Alex有正确的答案。每当你想用字符串来进行数学运算时,请远离键盘,直到诱惑消失。ASCII不是一种可接受的算术形式。 - Kris Jenkins

2
使用sprintf应该没问题。 sizeof type * 3 * CHAR_BIT / 8 + 2是打印类型为type的整数所需的足够大的缓冲区。如果您假设CHAR_BIT为8或仅关心无符号格式,则可以简化此表达式。其基本思想是每个字节在十进制(或八进制)中最多贡献3个数字,并且需要空间来存储符号和空终止符。

3
((CHAR_BIT * sizeof(type) - 1) / 3 + 2)将给出一个稍微小一些但仍然安全的数字。 - caf
好的观点。我的所有代码都假定 CHAR_BIT==8,所以我通常只使用 sizeof(type)*3+2,这样就不那么混乱了,但如果你想支持奇怪的字符大小,你的版本会更简洁和更紧凑。 - R.. GitHub STOP HELPING ICE

1
到目前为止,有一个答案交换了最后两个数字,还有一个答案交换了前两个数字...看起来"%0*u", OP_LEN强制输出为特定宽度,并提取的数字的重要性由OP_LEN预先确定。
假设OP_LEN是一个宏,我们可以通过以下方式得到10^(OP_LEN-2)
#define DIVISOR ( (int) ( 1.e ## OP_LEN * 0.01 ) )

然后,类似于@zneak的答案,

int extract_op( int instruction )
{
    instruction /= DIVISOR;
    int tens = (instruction / 10) % 10;
    int units = instruction % 10;
    return units * 10 + tens;
}

#undef DIVISOR

0

您可以将获取的数字存储到数组中。 这是Alex解释的代码。 在这里,我添加了一些变量。

int a[5];

int extract_op(unsigned int instruction)
{
int i=0;    
int first = 0;
    int second = 0;
    while(instruction) {
        second = first;
        first = instruction % 10;
        instruction /= 10;
    }
    a[i]=first;
}

这是适用于所有最多有5位数的整数的解决方案。 但如果你想使用动态数组,你可以使用链表。


0

应该也适用于0和小于0。

int extract_op( int instruction )
{
  int numd = 1;
  while( instruction /= 10 )
    ++numd;
  return numd;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接