使用更快的 I/O 方法

working of a faster I/O method

提问人:Sidak 提问时间:6/5/2014 最后编辑:Sidak 更新时间:1/22/2015 访问量:2283

问:

我正在研究更快的 I/O 方法来解决编程问题, 我发现了这种使用方法(虽然有风险,但仍然如此)。getchar_unlocked()

我环顾四周,但无法理解它是如何扫描整数值的 或者换句话说,这 4 行是什么意思以及它们在函数中是如何工作的 定义如下scanint()

#include<iostream>
#include<cstdio>
#define gc getchar_unlocked

void scanint(int &x)
{
    register int c = gc();
    x = 0;
    for(;(c<48 || c>57);c = gc());
    for(;c>47 && c<58;c = gc())   
        {x = (x<<1) + (x<<3) + c - 48;}
}

int main()
{   
    int n,k;
    scanint(n);
    scanint(k);
    int cnt=0;
    while(n--)
    {
     int num;
     scanint(num);
     if(num%k==0)cnt++; 
    }    
    printf("%d",cnt);
    return 0;             
}
C++ C IOstream 获取Char

评论

0赞 Spundun 6/5/2014
该计划的预期投入是多少?
0赞 πάντα ῥεῖ 6/5/2014
它读取单个值,直到找到非数字字符,并将数字添加到输出参数中。charint
2赞 stark 6/5/2014
代码扫描有符号的整数,但所有负值都失败。
1赞 chux - Reinstate Monica 6/5/2014
很好奇,当代码可以使用并且更容易理解时,为什么要使用?c<48 || c>57c<'0' || c>'9'
1赞 SeriousBusiness 6/5/2014
所使用的格式和约定整体上令人震惊

答:

4赞 mc110 6/5/2014 #1

该代码对 chars 和 进行了硬编码的 ASCII 值,因此它会跳过不在该范围内的任何内容(您感兴趣的四行中的第一个 for 循环)。'0''9'

然后,当它看到 - 范围内的字符时,它会将其运行总数乘以 10(通过将其向左移动一次以使其加倍,然后将其向左移动三次,这就像乘以 8 一样)并添加当前字符 - 的代码。'0''9''0'

1赞 haccks 6/5/2014 #2

线

for(;(c<48 || c>57);c = gc());  

读取不介于 之间的字符。'0''9'

线

for(;c>47 && c<58;c = gc())   

逐个读取 TO 之间的字符。'0''9'

线

{x = (x<<1) + (x<<3) + c - 48;}   

简单就等价于

x = 10 * x + c - 48;  

此函数的简化版本可以改写为:

#define gc getchar_unlocked
int read_int()
{
    char c = gc();
    while(c<'0' || c>'9')
       c = gc();
    int ret = 0;
    while(c>='0' && c<='9')
    {
        ret = 10 * ret + c - 48;
        c = gc();
    }
    return ret;
}  
2赞 Spundun 6/5/2014 #3

的 ASCII 值为 48,每个后续数字多一个。即 ->49、->50......等等。'0''1''2'

这样做的副作用是,如果你取一个字符数字,意思是介于 和 之间的内容,并从中减去 的 ASCII 值,那么你就会得到该数字的整数值。'0''9''0'

因此,在该行中,该部分将数字字符(ASCII编码字符)转换为0-9之间的数字,该数字表示该字符。x = (x<<1) + (x<<3) + c - 48;c-48

(x<<1)+(x<<3)与 ,(有关更多信息,请查看 http://en.wikipedia.org/wiki/Multiplication_algorithm#Shift_and_addHow can I multip and divide using only bit shift and add??) ) 实际上,这部分代码被不必要地混淆了。编译器可以通过许多不同的方式优化乘法,使其尽可能快,因此我们不需要手动实现位移。不过,这是一个有趣的大学水平的谜题。x * 10

for(;(c<48 || c>57);c = gc());此循环将忽略所有字符,直到它收到一个落在 to 范围内的字符。因此,如果用户通过键入空格或任何其他字符开始,它将被忽略。'0''9'

当代码到达行时,变量已初始化为用户键入的第一个数字。此循环将初始化留空,因此控制流将直接进入循环,并在键入每个字符时开始计算数字。for(;c>47 && c<58;c = gc()) {x = (x<<1) + (x<<3) + c - 48;}c

只要用户继续输入数字,循环就会继续,一旦用户输入数字以外的内容,循环就会终止,最终确定数字。

在用户继续输入数字之前,该行将一遍又一遍地执行,每次都是刚刚输入的字符。并将自身乘以并将新数字相加。x = (x<<1) + (x<<3) + c - 48;cx10

假设用户键入 2014。以下是 和 将要进步的值。cx

c = '2' #ASCII value 50
x = 2
c = '0' #ASCII value 48
x = 20
c = '1' #ASCII value 49
x = 201
c = '4' #ASCII value 52
x = 2014

HTH。