C 为什么32位编译器和64位编译器会对我的代码产生如此大的影响?
请原谅我英语不好 我写下了一些行来返回max、min、所有值的总和,并在输入五个整数时按升序排列所有值 在编写时,当我需要输入5个整数时,在声明一个INT数组时,我错误地编写了'num[4]'。 但当我使用TDM-GCC4.9.2 64位版本编译时,它工作起来没有任何问题。当我意识到并更改为TDM-GCC4.9.2 32位版本时,它并没有 这是我的全部代码C 为什么32位编译器和64位编译器会对我的代码产生如此大的影响?,c,arrays,32bit-64bit,C,Arrays,32bit 64bit,请原谅我英语不好 我写下了一些行来返回max、min、所有值的总和,并在输入五个整数时按升序排列所有值 在编写时,当我需要输入5个整数时,在声明一个INT数组时,我错误地编写了'num[4]'。 但当我使用TDM-GCC4.9.2 64位版本编译时,它工作起来没有任何问题。当我意识到并更改为TDM-GCC4.9.2 32位版本时,它并没有 这是我的全部代码 #include<stdio.h> int main() { int num[4],i,j,k,a,b,c,m,nu
#include<stdio.h>
int main()
{
int num[4],i,j,k,a,b,c,m,number,sum=0;
printf("This program returns max, min, sum of all values, and arranges all values in ascending order when five integers are input.\n");
printf("Please enter five integers.\n");
for(i=0;i<5;i++)
{
printf("Enter #%d\n",i+1);
scanf("%d",&num[i]);
}
//arrange all values
for(j=0;j<5;j++)
{
for(k=j+1;k<5;k++)
{
if(num[j]>num[k])
{
number=num[j];
num[j]=num[k];
num[k]=number;
}
}
}
//find maximum value
int max=num[0];
for(a=1;a<5;a++)
{
if(max<num[a])
{
max=num[a];
}
}
//find minimum value
int min=num[0];
for(b=1;b<5;b++)
{
if(min>num[b])
{
min=num[b];
}
}
//find sum of all values
for(c=0;c<5;c++)
{
sum=sum+num[c];
}
printf("Max Value : %d\n",max);//print max
printf("Min Value : %d\n",min);//print min
printf("Sum : %d\n",sum); //print sum
printf("In ascending order : "); //print all values in ascending order
for(m=0;m<5;m++)
{
printf("%d ",num[m]);
}
}
#包括
int main()
{
int num[4],i,j,k,a,b,c,m,number,sum=0;
printf(“当输入五个整数时,此程序返回所有值的最大值、最小值和,并按升序排列所有值。\n”);
printf(“请输入五个整数。\n”);
对于(i=0;i在堆栈上分配时,目标为64位(可能是Clang)的GCC将堆栈分配对齐到8字节
对于32位目标,它只需要使用4字节的填充
因此,当你编译64位的程序时,额外的四个字节被用来填充堆栈。这就是为什么当你访问最后一个整数时,它没有出错
要查看此操作,我们将创建一个测试文件
void test_func(){
int n[4];
int b=11;
对于(int i=0;i<4;i++){
n[i]=b;
}
}
我们将编译32位和64位
gcc -g -c -m64 test.c -o test_64.o
gcc -g -c -m32 test.c -o test_32.o
现在,我们将打印每个组件的反汇编
objdump -S test_64.o >test_64_dis.txt
objdump -S test_32.o >test_32_dis.txt
以下是64位版本的内容
test_64.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func>:
void func() {
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 30 sub $0x30,%rsp
c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
13: 00 00
15: 48 89 45 f8 mov %rax,-0x8(%rbp)
19: 31 c0 xor %eax,%eax
int n[4];
int b = 11;
1b: c7 45 dc 0b 00 00 00 movl $0xb,-0x24(%rbp)
for (int i = 0; i < 4; i++) {
22: c7 45 d8 00 00 00 00 movl $0x0,-0x28(%rbp)
29: eb 10 jmp 3b <func+0x3b>
n[i] = b;
2b: 8b 45 d8 mov -0x28(%rbp),%eax
2e: 48 98 cltq
30: 8b 55 dc mov -0x24(%rbp),%edx
33: 89 54 85 e0 mov %edx,-0x20(%rbp,%rax,4)
for (int i = 0; i < 4; i++) {
37: 83 45 d8 01 addl $0x1,-0x28(%rbp)
3b: 83 7d d8 03 cmpl $0x3,-0x28(%rbp)
3f: 7e ea jle 2b <func+0x2b>
}
}
41: 90 nop
42: 48 8b 45 f8 mov -0x8(%rbp),%rax
46: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4d: 00 00
4f: 74 05 je 56 <func+0x56>
51: e8 00 00 00 00 callq 56 <func+0x56>
56: c9 leaveq
57: c3 retq
test_32.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
void func() {
0: f3 0f 1e fb endbr32
4: 55 push %ebp
5: 89 e5 mov %esp,%ebp
7: 83 ec 28 sub $0x28,%esp
a: e8 fc ff ff ff call b <func+0xb>
f: 05 01 00 00 00 add $0x1,%eax
14: 65 a1 14 00 00 00 mov %gs:0x14,%eax
1a: 89 45 f4 mov %eax,-0xc(%ebp)
1d: 31 c0 xor %eax,%eax
int n[4];
int b = 11;
1f: c7 45 e0 0b 00 00 00 movl $0xb,-0x20(%ebp)
for (int i = 0; i < 4; i++) {
26: c7 45 dc 00 00 00 00 movl $0x0,-0x24(%ebp)
2d: eb 0e jmp 3d <func+0x3d>
n[i] = b;
2f: 8b 45 dc mov -0x24(%ebp),%eax
32: 8b 55 e0 mov -0x20(%ebp),%edx
35: 89 54 85 e4 mov %edx,-0x1c(%ebp,%eax,4)
for (int i = 0; i < 4; i++) {
39: 83 45 dc 01 addl $0x1,-0x24(%ebp)
3d: 83 7d dc 03 cmpl $0x3,-0x24(%ebp)
41: 7e ec jle 2f <func+0x2f>
}
}
43: 90 nop
44: 8b 45 f4 mov -0xc(%ebp),%eax
47: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
4e: 74 05 je 55 <func+0x55>
50: e8 fc ff ff ff call 51 <func+0x51>
55: c9 leave
56: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret
test_64.o:文件格式elf64-x86-64
第节的分解。正文:
0000000000000000 :
void func(){
0:f3 0f 1e fa endbr64
4:55%rbp
5:48 89 e5 mov%rsp,%rbp
8:48 83 ec 30分$0x30,%rsp
c:64 48 8b 04 25 28 00 mov%fs:0x28,%rax
13: 00 00
15:488945F8MOV%rax,-0x8(%rbp)
19:31 c0 xor%eax,%eax
int n[4];
int b=11;
1b:c7 45 dc 0b 00移动$0xb,-0x24(%rbp)
对于(int i=0;i<4;i++){
22:c7 45 d8 00动产$0x0,-0x28(%rbp)
29:eb 10 jmp 3b
n[i]=b;
2b:8b 45 d8 mov-0x28(%rbp),%eax
2e:48 98 cltq
30:8b 55直流mov-0x24(%rbp),%edx
33:89 54 85 e0 mov%edx,-0x20(%rbp,%rax,4)
对于(int i=0;i<4;i++){
37:83 45 d8 01添加$0x1,-0x28(%rbp)
3b:83 7d d8 03 cmpl$0x3,-0x28(%rbp)
3f:7e ea jle 2b
}
}
41:90不
42:48 8b 45 f8 mov-0x8(%rbp),%rax
46:6448330425800异或%fs:0x28,%rax
4d:00
4f:74 05 56
51:e8 00呼叫56
56:c9/Q
57:c3 retq
这是32位版本
test_64.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func>:
void func() {
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 30 sub $0x30,%rsp
c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
13: 00 00
15: 48 89 45 f8 mov %rax,-0x8(%rbp)
19: 31 c0 xor %eax,%eax
int n[4];
int b = 11;
1b: c7 45 dc 0b 00 00 00 movl $0xb,-0x24(%rbp)
for (int i = 0; i < 4; i++) {
22: c7 45 d8 00 00 00 00 movl $0x0,-0x28(%rbp)
29: eb 10 jmp 3b <func+0x3b>
n[i] = b;
2b: 8b 45 d8 mov -0x28(%rbp),%eax
2e: 48 98 cltq
30: 8b 55 dc mov -0x24(%rbp),%edx
33: 89 54 85 e0 mov %edx,-0x20(%rbp,%rax,4)
for (int i = 0; i < 4; i++) {
37: 83 45 d8 01 addl $0x1,-0x28(%rbp)
3b: 83 7d d8 03 cmpl $0x3,-0x28(%rbp)
3f: 7e ea jle 2b <func+0x2b>
}
}
41: 90 nop
42: 48 8b 45 f8 mov -0x8(%rbp),%rax
46: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4d: 00 00
4f: 74 05 je 56 <func+0x56>
51: e8 00 00 00 00 callq 56 <func+0x56>
56: c9 leaveq
57: c3 retq
test_32.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
void func() {
0: f3 0f 1e fb endbr32
4: 55 push %ebp
5: 89 e5 mov %esp,%ebp
7: 83 ec 28 sub $0x28,%esp
a: e8 fc ff ff ff call b <func+0xb>
f: 05 01 00 00 00 add $0x1,%eax
14: 65 a1 14 00 00 00 mov %gs:0x14,%eax
1a: 89 45 f4 mov %eax,-0xc(%ebp)
1d: 31 c0 xor %eax,%eax
int n[4];
int b = 11;
1f: c7 45 e0 0b 00 00 00 movl $0xb,-0x20(%ebp)
for (int i = 0; i < 4; i++) {
26: c7 45 dc 00 00 00 00 movl $0x0,-0x24(%ebp)
2d: eb 0e jmp 3d <func+0x3d>
n[i] = b;
2f: 8b 45 dc mov -0x24(%ebp),%eax
32: 8b 55 e0 mov -0x20(%ebp),%edx
35: 89 54 85 e4 mov %edx,-0x1c(%ebp,%eax,4)
for (int i = 0; i < 4; i++) {
39: 83 45 dc 01 addl $0x1,-0x24(%ebp)
3d: 83 7d dc 03 cmpl $0x3,-0x24(%ebp)
41: 7e ec jle 2f <func+0x2f>
}
}
43: 90 nop
44: 8b 45 f4 mov -0xc(%ebp),%eax
47: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
4e: 74 05 je 55 <func+0x55>
50: e8 fc ff ff ff call 51 <func+0x51>
55: c9 leave
56: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret
test_32.o:文件格式elf32-i386
第节的分解。正文:
00000000 :
void func(){
0:f3 0f 1e fb endbr32
4:55%推压ebp
5:89 e5 mov%esp,%ebp
7:83 ec 28子$0x28,%esp
a:e8 fc ff呼叫b
f:05 01 00添加$0x1,%eax
14:65 a1 14 00 mov%gs:0x14%eax
1a:89 45 f4 mov%eax,-0xc(%ebp)
1d:31 c0异或%eax,%eax
int n[4];
int b=11;
1f:c7 45 e0 0b 00移动$0xb,-0x20(%ebp)
对于(int i=0;i<4;i++){
26:c7 45 dc 00移动$0x0,-0x24(%ebp)
2d:eb 0e jmp 3d
n[i]=b;
2f:8b 45直流mov-0x24(%ebp),%eax
32:8b 55 e0 mov-0x20(%ebp),%edx
35:89 54 85 e4 mov%edx,-0x1c(%ebp,%eax,4)
对于(int i=0;i<4;i++){
39:83 45 dc 01地址$0x1,-0x24(%ebp)
3d:83 7d dc 03 cmpl$0x3,-0x24(%ebp)
41:7e ec jle 2f
}
}
43:90不
44:8b 45 f4 mov-0xc(%ebp),%eax
47:65 33 05 14 00 00异或%gs:0x14,%eax
4e:74 05 55
50:e8 fc ff呼叫51
55:c9离开
56:c3 ret
.text.\uuux86.get\u pc\u thunk.ax节的反汇编:
00000000 :
0:8b 04 24 mov(%esp),%eax
3:c3 ret
您可以看到编译器分别生成24个字节和20个字节,如果您在变量声明之后查看的话
关于您要求的建议/提示,一个好的起点是启用所有编译器警告并将其视为错误。在GCC和Clang中,您可以使用-Wall-Wextra-Werror-Wfatal errors
但是,如果您使用的是MSVC编译器,我不建议您这样做。MSVC编译器通常会对其分发的头文件中的声明发出警告。通过分析生成的程序集,其他答案涵盖了实际发生的情况,但真正相关的解释是:索引超出数组边界是未定义的C.中的d行为。故事就到此结束
UB表示代码是“允许的”按照C标准做任何事情。它可以在每次运行时做不同的事情。它可以做你想做的事情,而不会产生不良影响。它可以做你想做的事情,但之后一些完全不相关的事情会以一种有趣的方式发生。编译器、操作系统,甚至月相都可能产生影响。或者不是
通常,在C级思考未定义行为的实际情况是没有用的。当然,您可以生成特定编译的程序集输出,并检查它的功能,但这是该编译的结果