首先或直接声明变量,有区别吗?

时间:2018-01-11 09:41:18

标签: c assembly

首先声明变量然后分配值或直接声明并在编译函数中指定值之间是否存在差异?编译的函数是否执行相同的工作?例如,它是否仍然读取参数,声明变量然后分配值,或者编译版本中的两个示例之间是否存在差异?

示例:

    void foo(u32 value) {

      u32 extvalue = NULL;

      extvalue = value;

    }

比较
    void foo(u32 value) {

    u32 extvalue = value;

    }

我的印象是,如果你查看编译后的代码,这两个函数之间没有区别,例如它们看起来一样,我将无法分辨哪个是哪个。

2 个答案:

答案 0 :(得分:4)

这取决于编译器和&当然是优化水平。

一个愚蠢的编译器/低优化级别,当它看到:

  u32 extvalue = NULL;
  extvalue = value;

可以在下一行设置为NULL然后设置为value

由于extvalue未在中间使用,NULL初始化无用,大多数编译器直接设置为value作为简单优化

请注意,声明变量实际上并不是指令。编译器只是分配自动内存来存储这个变量。

我测试了一个简单的代码,有或没有赋值,结果是差异 使用gcc编译器6.2.1与-O0(不要优化任何东西)标志时的标志:

 #include <stdio.h>
 void foo(int value) {

      int extvalue = 0;
      extvalue = value;

      printf("%d",extvalue);
    }

拆卸:

Disassembly of section .text:

00000000 <_foo>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 28                sub    $0x28,%esp
   6:   c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%ebp)  <=== here we see the init
   d:   8b 45 08                mov    0x8(%ebp),%eax
  10:   89 45 f4                mov    %eax,-0xc(%ebp)
  13:   8b 45 f4                mov    -0xc(%ebp),%eax
  16:   89 44 24 04             mov    %eax,0x4(%esp)
  1a:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  21:   e8 00 00 00 00          call   26 <_foo+0x26>
  26:   c9                      leave
  27:   c3                      ret

现在:

 void foo(int value) {

      int extvalue;
      extvalue = value;

      printf("%d",extvalue);
    }

拆卸:

Disassembly of section .text:

00000000 <_foo>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 28                sub    $0x28,%esp
   6:   8b 45 08                mov    0x8(%ebp),%eax
   9:   89 45 f4                mov    %eax,-0xc(%ebp)
   c:   8b 45 f4                mov    -0xc(%ebp),%eax
   f:   89 44 24 04             mov    %eax,0x4(%esp)
  13:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  1a:   e8 00 00 00 00          call   1f <_foo+0x1f>
  1f:   c9                      leave
  20:   c3                      ret
  21:   90                      nop
  22:   90                      nop
  23:   90                      nop

0 init已经消失。在这种情况下,编译器没有优化初始化。

如果我切换到-O2(良好的优化级别),那么在这两种情况下代码都是相同的,编译器发现初始化是不必要的(仍然是静默的,没有警告):

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 18                sub    $0x18,%esp
   6:   8b 45 08                mov    0x8(%ebp),%eax
   9:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  10:   89 44 24 04             mov    %eax,0x4(%esp)
  14:   e8 00 00 00 00          call   19 <_foo+0x19>
  19:   c9                      leave
  1a:   c3                      ret

答案 1 :(得分:1)

我在Godbolt尝试了这些功能:

void foo(uint32_t value)
{
      uint32_t extvalue = NULL;
      extvalue = value;
}

void bar(uint32_t value)
{
      uint32_t extvalue = value;
}

我移植到实际类型uint32_t而不是u32这不是标准的。由x86-64 GCC 6.3生成的非优化组件是:

foo(unsigned int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-20], edi
        mov     DWORD PTR [rbp-4], 0
        mov     eax, DWORD PTR [rbp-20]
        mov     DWORD PTR [rbp-4], eax
        nop
        pop     rbp
        ret

bar(unsigned int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-20], edi
        mov     eax, DWORD PTR [rbp-20]
        mov     DWORD PTR [rbp-4], eax
        nop
        pop     rbp
        ret

很明显,非优化代码保留了(奇怪的,正如其他人指出的那样,因为它没有写入指针)NULL赋值,这当然是毫无意义的。

我投票支持第二个,因为它更短(在阅读代码时更少留在一个人的头脑中),并且从不允许/推荐在用适当的值覆盖之前,将NULL设置为无意义。我会认为这是一个错误,因为你正在说/做一些你并不意味着的事情。