为什么要使用char数组字节transffer的访问元素

时间:2018-03-13 00:01:37

标签: c assembly memory-management

让我们考虑一下这个非常简单的代码

int main(void)
{
    char buff[500];
    int i;
    for (i=0; i<500; i++)
    {
        (buff[i])++;
    }   
}

因此,它只需要经过500个字节并递增它。此代码在x86-64架构上使用gcc编译,并使用objdump -D实用程序进行反汇编。看看反汇编的代码,我发现数据是逐字节地从内存传输到寄存器的(参见,movzbl指令用于从内存中获取数据,mov%dl用于在内存中存储数据)

00000000004004ed <main>:
  4004ed:       55                      push   %rbp
  4004ee:       48 89 e5                mov    %rsp,%rbp
  4004f1:       48 81 ec 88 01 00 00    sub    $0x188,%rsp
  4004f8:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4004ff:       eb 20                   jmp    400521 <main+0x34>
  400501:       8b 45 fc                mov    -0x4(%rbp),%eax
  400504:       48 98                   cltq   
  400506:       0f b6 84 05 00 fe ff    movzbl -0x200(%rbp,%rax,1),%eax
  40050d:       ff 
  40050e:       8d 50 01                lea    0x1(%rax),%edx
  400511:       8b 45 fc                mov    -0x4(%rbp),%eax
  400514:       48 98                   cltq   
  400516:       88 94 05 00 fe ff ff    mov    %dl,-0x200(%rbp,%rax,1)
  40051d:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
  400521:       81 7d fc f3 01 00 00    cmpl   $0x1f3,-0x4(%rbp)
  400528:       7e d7                   jle    400501 <main+0x14>
  40052a:       c9                      leaveq 
  40052b:       c3                      retq   
  40052c:       0f 1f 40 00             nopl   0x0(%rax)

看起来它有一些性能影响,因为在这种情况下,您必须访问内存500次才能读取,500次才能存储。我知道缓存系统会以某种方式处理它,但无论如何。 我的问题是为什么我们不能加载四字,只做一些位操作来增加该四字的每个字节,然后将其推回内存?显然,它需要一些额外的逻辑来处理小于四字的数据的最后部分和一些额外的寄存器。但是这种方法将大大减少内存访问的数量,这是最昂贵的操作。可能我没有看到一些阻碍这种优化的障碍。所以,在这里得到一些解释会很棒。

2 个答案:

答案 0 :(得分:1)

为什么不应该这样做的原因:想象一下,如果char恰好是无符号的(使溢出有定义的行为)并且你有一个字节0xFF后面(或前面,取决于字节顺序) 0x1

一次增加一个字节,最终0xFF变为0x000x01变为0x02。但是,如果您一次只加载4或8个字节并添加0x01010101(或等效的8个字节)来实现相同的结果,0xFF将溢出到0x01,因此您最终会0x000x03,而不是0x000x02

签名char通常也会出现类似问题;有符号的溢出和截断规则(或缺少它)使它更复杂,但要点是一次增加一个字节限制对该字节的影响,没有交叉字节“干扰”。

答案 1 :(得分:1)

当你在没有优化的情况下编译时,编译器会对代码进行更直接的代码转换,部分原因是当你在调试器中单步执行代码时,这些步骤对应于你的代码。

如果启用优化,则装配可能看起来完全不同。

此外,您的程序会通过读取未初始化的char来导致未定义的行为。