【问题标题】:working of a faster I/O method更快的 I/O 方法的工作
【发布时间】:2014-07-25 15:39:11
【问题描述】:

我正在研究更快的 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;             
}

【问题讨论】:

  • 这个程序的预期输入是什么?
  • 它读取单个char 值直到找到非数字字符,并将数字添加到输出int 参数。
  • 代码扫描有符号整数,但对所有负值都失败。
  • 很好奇,当代码可以使用c&lt;'0' || c&gt;'9' 并且更容易理解时,为什么还要使用c&lt;48 || c&gt;57
  • 所使用的格式和约定总体上令人震惊

标签: c++ c iostream getchar


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    这样做的副作用是,如果你取一个字符数字,意思是'0''9' 之间的某个数字,然后从中减去'0' 的ASCII 值,那么你会得到那个数字的整数值。

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

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

    for(;(c&lt;48 || c&gt;57);c = gc()); 这个循环将忽略所有字符,直到它接收到一个在'0''9' 范围内的字符。因此,如果用户从输入空格或任何其他字符开始,它将被简单地忽略。

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

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

    在用户不断输入数字之前,x = (x&lt;&lt;1) + (x&lt;&lt;3) + c - 48; 行将不断重复执行,每次c 都是刚刚输入的字符。 x 将自己乘以 10 并添加新数字。

    假设用户输入 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。

    【讨论】:

      【解决方案3】:

      线

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

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

      线

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

      一一读取'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;
      }  
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-16
        • 2017-03-26
        • 1970-01-01
        • 2023-03-10
        • 2021-08-17
        • 2014-05-17
        • 2011-09-25
        • 2013-07-20
        相关资源
        最近更新 更多