带括号和不带括号的switch语句

时间:2012-12-24 13:57:41

标签: c assembly

我只是编写一个简单的程序来学习c编程

  1 #include<stdio.h>
  2 
  3 int main()
  4 {
  5         int a = 5;
  6         switch(a)
  7         {
  8                 case 0:
  9                         {
 10                         ;
 11                         int a = 10;
 12                         printf("%d\n",a);
 13                         break;
 14                         }
 15                 default :
 16                         printf("%d",a);
 17 
 18         }
 19         return 0;
 20 }

输出: 5 当我忘记支撑时,它变成了:

  8                 case 0:
  9                        
 10                         ;
 11                         int a = 10;
 12                         printf("%d\n",a);
 13                         break;
 14                         

输出: 0

我对此感到困惑,并尝试编译和调试:


-   1    0x000000000040051c <+0>:     push   %rbp                          |-   1    0x000000000040051c <+0>:     push   %rbp
|   2    0x000000000040051d <+1>:     mov    %rsp,%rbp                     ||   2    0x000000000040051d <+1>:     mov    %rsp,%rbp
|   3    0x0000000000400520 <+4>:     sub    $0x10,%rsp                    ||   3    0x0000000000400520 <+4>:     sub    $0x10,%rsp
|   4 => 0x0000000000400524 <+8>:     movl   $0x5,-0x8(%rbp)               ||   4 => 0x0000000000400524 <+8>:     movl   $0x5,-0x8(%rbp)
|   5    0x000000000040052b <+15>:    mov    -0x8(%rbp),%eax               ||   5    0x000000000040052b <+15>:    mov    -0x8(%rbp),%eax
|   6    0x000000000040052e <+18>:    test   %eax,%eax                     ||   6    0x000000000040052e <+18>:    test   %eax,%eax
|   7    0x0000000000400530 <+20>:    jne    0x40054f <main+51>            ||   7    0x0000000000400530 <+20>:    jne    0x40054f <main+51>
|   8    0x0000000000400532 <+22>:    movl   $0xa,-0x4(%rbp)               ||   8    0x0000000000400532 <+22>:    movl   $0xa,-0x4(%rbp)
    9    0x0000000000400539 <+29>:    mov    -0x4(%rbp),%eax               |    9    0x0000000000400539 <+29>:    mov    -0x4(%rbp),%eax
   10    0x000000000040053c <+32>:    mov    %eax,%esi                     |   10    0x000000000040053c <+32>:    mov    %eax,%esi
   11    0x000000000040053e <+34>:    mov    $0x400614,%edi                |   11    0x000000000040053e <+34>:    mov    $0x400614,%edi
   12    0x0000000000400543 <+39>:    mov    $0x0,%eax                     |   12    0x0000000000400543 <+39>:    mov    $0x0,%eax
   13    0x0000000000400548 <+44>:    callq  0x4003f0 <printf@plt>         |   13    0x0000000000400548 <+44>:    callq  0x4003f0 <printf@plt>
   14    0x000000000040054d <+49>:    jmp    0x400563 <main+71>            |   14    0x000000000040054d <+49>:    jmp    0x400563 <main+71>
   15    0x000000000040054f <+51>:    mov    -0x8(%rbp),%eax               |   15    0x000000000040054f <+51>:    mov    -0x4(%rbp),%eax              
   16    0x0000000000400552 <+54>:    mov    %eax,%esi                     |   16    0x0000000000400552 <+54>:    mov    %eax,%esi
   17    0x0000000000400554 <+56>:    mov    $0x400618,%edi                |   17    0x0000000000400554 <+56>:    mov    $0x400618,%edi
   18    0x0000000000400559 <+61>:    mov    $0x0,%eax                     |   18    0x0000000000400559 <+61>:    mov    $0x0,%eax
   19    0x000000000040055e <+66>:    callq  0x4003f0 <printf@plt>         |   19    0x000000000040055e <+66>:    callq  0x4003f0 <printf@plt>
   20    0x0000000000400563 <+71>:    mov    $0x0,%eax                     |   20    0x0000000000400563 <+71>:    mov    $0x0,%eax
   21    0x0000000000400568 <+76>:    leaveq                               |   21    0x0000000000400568 <+76>:    leaveq 
+  22 +--  2 lines: 0x0000000000400569 <+77>: retq   ----------------------|+  22 +--  2 lines: 0x0000000000400569 <+77>: retq   ---------------------

有点不同但至关重要:

$ diff with.txt without.txt 
15c15
<    0x000000000040054f <+51>:  mov    -0x8(%rbp),%eax
---
>    0x000000000040054f <+51>:  mov    -0x4(%rbp),%eax

先谢谢了。


更新 我从中学到了gcc -Wall始终是一个好习惯的教训

3 个答案:

答案 0 :(得分:3)

所以你必须真正努力达到这一点,因为如果代码中没有额外的分号,它将无法编译。这让我觉得这是一个专门用于面试或其他一些例子的人为例子。

在第二个示例中,变量a(inner)是在switch braces {}处创建的,但它没有在该级别初始化,因为初始化仅在0:code的情况下。所以a的值完全是随机的(恰好是零)

无论哪种方式,编码风格都不好!如果引入变量,请不要忘记在case语句中使用大括号。 [在g ++中你实际上得到一个错误“跳转到case-label跨越'int a'初始化]

因此,首先,gcc -Wall将对“未经注册的变量”发出警告。

如果我们考虑这个例子:

int a = 111;

int main()
{
    int a = 2;

    printf("a=%d\n", a);
}

我认为没有人会争辩“我们的意思是什么”,对吗?

或者如果我们有:

 int main()
 {
    int x = 12;

    int a = 11;
    if(x == 12)
    {
         int a = 2;

         printf("a=%d\n", a);
    }

  }

同样,这里的情况非常明显,对吧?

如果我们重写您的代码以显示实际发生的事情:

int main()
{
        int a = 5;
        switch(a)
        { 
           int a;
               case 0:
                a=10 ;
                        printf("%d\n",a);
                        break;
                default :
                        printf("%d",a);

        }
        return 0;
}

现在,这在语义上与第二个变体中的代码相同。它看起来有点不同!

答案 1 :(得分:1)

如果没有'{}',你只需跳过内部变量'a'的初始化(虽然它仍然被定义),所以你得到一个未初始化的'a',在这种情况下,它是'0'。< / p>

BTW,使用'{}',它应该输出'5'。

答案 2 :(得分:1)

我很欣赏这篇文章中使用的程序集 它有助于以更好的方式理解代码。

现在发生了什么。

让我们讨论这个问题

案例1:

#include<stdio.h>

int main()
{
    int a=5;

    switch(a)
    {
        case 0:
        {
            ;
            int a = 10;
            printf("%d in case 0\n",a);
            break;
            ;
        }
        default:
            printf("%d in default case\n",a);
            break;
    }
    return 0;
}

案例1大会:

    .file   "test1.c"
    .section    .rodata
.LC0:
    .string "%d in case 0\n"
.LC1:
    .string "%d in default case\n"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $5, 28(%esp)
    movl    28(%esp), %eax
    testl   %eax, %eax
    jne .L6
.L3:
    movl    $10, 24(%esp)
    movl    $.LC0, %eax
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    jmp .L4
.L6:
    movl    $.LC1, %eax
    movl    28(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
.L4:
    movl    $0, %eax
    leave
    ret

案例1 o / p: root@local-host#./a.out 默认情况下为5

说明: 这是预期的,因为您可以在程序集中看到语句

movl    $5, 28(%esp)

我们正在复制/移动5到28(%esp)位置

movl    28(%esp), %eax
testl   %eax, %eax its a switch equivalent
主标签中的

这里28(%esp)只是a(int a = 5;)

的值

在L3处,所有24(%esp)用于存储值10 请注意,ebx寄存器在这里用作新的a。 而L6是默认情况,现在看下面的语句 movl 28(%esp),%edx 这里28(%esp)(这只是a的值)被复制到o / p因此我们的o / p是预期的。

案例2:

包括

int main()     {     int a = 5;

switch(a)
    {
    case 0:
        ;
        int a = 10;
        printf("%d in case 0\n",a);
        break;
    default:
        printf("%d in default case\n",a);
        break;
    }

返回0;     }

案例2大会:     .file&#34; test1.c&#34;     .section .rodata .LC0:     .string&#34;%d,如果是0 \ n&#34; .LC1:     .string&#34;%d默认情况下\ n&#34;     。文本 .globl主要     .type main,@ function 主要:     pushl%ebp     movl%esp,%ebp     andl $ -16,%esp     subl $ 32,%esp     movl $ 5,28(%esp)     movl 28(%esp),%eax     testl%eax,%eax     jne .L6 .L3:     movl $ 10,24(%esp)     movl $ .LC0,%eax     movl 24(%esp),%edx     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf     jmp .L4 .L6:     movl $ .LC1,%eax     movl 24(%esp),%edx     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf .L4:     movl $ 0,%eax     离开     保留

案例2 o / p: root@local-host#./a.out 默认情况下为134513723

垃圾值 让我在这里解释一下,28(%esp)是a的值 现在仔细看看L3和L6标签 以下是您在代码中找到的24%esp,只有在案例0满足时才创建a。 由于不满足0的情况,24%esp将不会被初始化,即a = 10;

我们将默认为L6,我们正试图获得24%的esp值,这还没有 初始化(但它是一个有效的位置)因为我们没有进入案例0。 这就是为什么我们获得垃圾价值。

案例3:

包括

int main()     {     int a = 5;

switch(a)
    {
    case 5:
        ;
        int a = 10;
        printf("%d in case 5\n",a);
    default:
        printf("%d in default case\n",a);
        break;
    }

返回0;     }

案例3汇编:     .file&#34; test1.c&#34;     .section .rodata .LC0:     .string&#34;%d在案例5 \ n&#34; .LC1:     .string&#34;%d默认情况下\ n&#34;     。文本 .globl主要     .type main,@ function 主要:     pushl%ebp     movl%esp,%ebp     andl $ -16,%esp     subl $ 32,%esp     movl $ 5,28(%esp)     movl 28(%esp),%eax     cmpl $ 5,%eax     jne .L2 .L3:     movl $ 10,24(%esp)     movl $ .LC0,%eax     movl 24(%esp),%edx     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf .L2:     movl $ .LC1,%eax     movl 24(%esp),%edx     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf     movl $ 0,%eax     离开     保留

案例3 o / p: root@local-host#./a.out 情况0为10 10默认情况下

在这里您可以看到新的a创建于24(%esp)并在案例5中初始化,因此值将相同 在默认位置。

在L3标签中 movl 24(%esp),%edx 在L2标签中 movl 24(%esp),%edx

因此使用相同的位置24(%esp),它在cse 5创建并初始化并流经默认值。

案例4:

包括

int main()     {     int a = 5;

switch(a)
    {
    case 0:
        ;
        static int a = 10;
        printf("%d in case 0\n",a);
        break;
    default:
        printf("%d in default\n",a);
        break;
    }

返回0;     }

案例4汇编:     .file&#34; test1.c&#34;     .section .rodata .LC0:     .string&#34;%d,如果是0 \ n&#34; .LC1:     .string&#34;%d默认\ n&#34;     。文本 .globl主要     .type main,@ function 主要:     pushl%ebp     movl%esp,%ebp     andl $ -16,%esp     subl $ 32,%esp     movl $ 5,28(%esp)     movl 28(%esp),%eax     testl%eax,%eax     jne .L6 .L3:     movl a.1706,%edx     movl $ .LC0,%eax     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf     jmp .L4 .L6:     movl a.1706,%edx     movl $ .LC1,%eax     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf .L4:     movl $ 0,%eax     离开     RET     .size main,。-main     。数据     .align 4     .type a.1706,@ object     .size a.1706,4 a.1706:     .long 10     .ident&#34; GCC:(Ubuntu / Linaro 4.4.4-14ubuntu5.1)4.4.5&#34;     .section .note.GNU-stack,&#34;&#34;,@ progbits

案例4 O / p: root@local-host#./a.out 10默认

这是有道理的,因为静态类型是全局的并且存储在数据部分中 在执行期间,全局值,即静态int a在数据部分中并初始化为10.

案例5:

包括

int main()     {     int a = 5;

switch(a)
    {
    case 0:
        {
        ;
        static int a = 10;
        printf("%d in case 0\n",a);
        break;
        }
    default:
        printf("%d in default\n",a);
        break;
    }

返回0;     }

案例5大会:     .file&#34; test1.c&#34;     .section .rodata .LC0:     .string&#34;%d,如果是0 \ n&#34; .LC1:     .string&#34;%d默认\ n&#34;     。文本 .globl主要     .type main,@ function 主要:     pushl%ebp     movl%esp,%ebp     andl $ -16,%esp     subl $ 32,%esp     movl $ 5,28(%esp)     movl 28(%esp),%eax     testl%eax,%eax     jne .L6 .L3:     movl a.1706,%edx     movl $ .LC0,%eax     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf     jmp .L4 .L6:     movl $ .LC1,%eax     movl 28(%esp),%edx     movl%edx,4(%esp)     movl%eax,(%esp)     叫printf .L4:     movl $ 0,%eax     离开     RET     .size main,。-main     。数据     .align 4     .type a.1706,@ object     .size a.1706,4 a.1706:     .long 10     .ident&#34; GCC:(Ubuntu / Linaro 4.4.4-14ubuntu5.1)4.4.5&#34;     .section .note.GNU-stack,&#34;&#34;,@ progbits

案例5 O / p: root@local-host#./a.out 5默认

这里由于&#39; {&#39;分隔符静态值在汇编程序阶段被限制为case 0 默认情况下仍然是从28(%esp)获得的值,这与汇编时的预期一样,情况0为静态且超出默认范围。

您可以通过删除中断并应用&#39; {&#39;来尝试不同的格式。 &#39;}&#39;分隔符

永远不要忘记在范围和汇编代码方面查看。