我只是编写一个简单的程序来学习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
始终是一个好习惯的教训
答案 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;分隔符
永远不要忘记在范围和汇编代码方面查看。