c++ - 编译器可以删除 `memset` 函数调用吗?

标签 c++ c memory

我已阅读 here如果编译器知道传递的内存缓冲区不再使用,它​​可以自由地删除对 memset 的调用。这怎么可能?在我看来(从核心语言的角度来看)memset 只是一个常规函数,编译器无权假设其中发生的任何事情都没有副作用。

linked article他们展示了 Visual C++ 10 如何删除 memset。我知道 Microsoft 编译器在标准合规性方面并不领先,所以我问 - 是根据标准,还是只是 msvc-ism?如果符合标准,请详细说明;)

编辑: @库比

以下代码:

void testIt(){
    char foo[1234];
    for (int i=0; i<1233; i++){
        foo[i] = rand()%('Z'-'A'+1)+'A';
    }
    foo[1233]=0;
    printf(foo);
    memset(foo, 0, 1234);
}

在mingw下用行编译:

g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE -o main.o main.cpp
g++ -Wl,-s -Wl,-subsystem,console -mthreads -o main.exe main.o
objdump -d -M intel -S main.exe > dump.asm

给出输出:

 4013b0:    55                      push   ebp
 4013b1:    89 e5                   mov    ebp,esp
 4013b3:    57                      push   edi
 4013b4:    56                      push   esi
 4013b5:    53                      push   ebx
 4013b6:    81 ec fc 04 00 00       sub    esp,0x4fc
 4013bc:    31 db                   xor    ebx,ebx
 4013be:    8d b5 16 fb ff ff       lea    esi,[ebp-0x4ea]
 4013c4:    bf 1a 00 00 00          mov    edi,0x1a
 4013c9:    8d 76 00                lea    esi,[esi+0x0]
 4013cc:    e8 6f 02 00 00          call   0x401640
 4013d1:    99                      cdq    
 4013d2:    f7 ff                   idiv   edi
 4013d4:    83 c2 41                add    edx,0x41
 4013d7:    88 14 1e                mov    BYTE PTR [esi+ebx*1],dl
 4013da:    43                      inc    ebx
 4013db:    81 fb d1 04 00 00       cmp    ebx,0x4d1
 4013e1:    75 e9                   jne    0x4013cc
 4013e3:    c6 45 e7 00             mov    BYTE PTR [ebp-0x19],0x0
 4013e7:    89 34 24                mov    DWORD PTR [esp],esi
 4013ea:    e8 59 02 00 00          call   0x401648
 4013ef:    81 c4 fc 04 00 00       add    esp,0x4fc
 4013f5:    5b                      pop    ebx
 4013f6:    5e                      pop    esi
 4013f7:    5f                      pop    edi
 4013f8:    c9                      leave  
 4013f9:    c3                      ret   

在 4013ea 行有 memset 调用,所以 mingw 没有删除它。由于 mingw 确实是 windows 皮肤中的 GCC,我想 GCC 也是如此 - 我会在重新启动到 linux 时检查它。

仍然无法找到这样的编译器?

EDIT2:

我刚刚发现了 GCC 的 __attribute__ ((pure))。所以并不是编译器知道 memset 的一些特殊之处并忽略它,只是它的标题中允许使用它 - 使用它的程序员也应该看到它;)我的 mingw 在 memset 中没有这个属性声明,因此无论如何它都不会从大会中消失 - 正如我所期望的那样。我将不得不对此进行调查。

最佳答案

“编译器无权假设它内部发生的任何事情都没有副作用。”

没错。但是如果编译器实际上知道实际上在其中发生了什么并且可以确定它确实没有没有副作用,那么就不需要假设了。

这就是几乎所有编译器优化的工作方式。代码显示“X”。编译器确定如果“Y”为真,那么它可以将代码“X”替换为代码“Z”并且不会有可检测的差异。它确定“Y”为真,然后将“X”替换为“Z”。

例如:

void func()
{
  int j = 2;
  foo();
  if (j == 2) bar();
   else baz();
}

编译器可以将其优化为 foo();酒吧();。编译器可以看到 foo 不能合法地修改 j 的值。如果 foo() 以某种方式神奇地找出 j 在堆栈上的位置并修改它,那么优化将改变代码的行为,但这是程序员使用的错误“魔法”。

void func()
{
  int j = 2;
  foo(&j);
  if (j == 2) bar();
   else baz();
}

现在不能了,因为 foo 可以合法地修改 j 的值而无需任何魔法。 (假设编译器不能查看 foo 内部,在某些情况下它可以。)

如果您使用“魔术”,那么编译器可以进行优化以破坏您的代码。遵守规则,不要使用魔法。

在您链接到的示例中,代码依赖于编译器将特定值放入一个永远不会访问并立即不存在的变量中。编译器不需要做任何对你的代码运行没有影响的事情。

可能影响代码的唯一方法是它是否查看堆栈的未分配部分或依赖于堆栈上具有先前具有的值的新分配。要求编译器这样做会使大量优化变得不可能,包括用寄存器替换局部变量。

关于c++ - 编译器可以删除 `memset` 函数调用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15538366/

相关文章:

c - 错误 - pthread_mutex_lock() 失败 : Invalid argument

c - 指针和数组在内存方面的区别

c - 为什么变量的地址变小了?

C++ 位掩码 72 位变量并切断前导和尾随零 - 请帮忙

c++ - Cout 不打印号码

c - libpcap 中的确认号

c - 文本文件内的文本单词搜索 - C 编程

java - 如何在 java 中执行内存高效的数组排序?

c++ - 模板参数推导失败

使用流和缓冲区的 C++ 最佳实践