对不起,我的英语不好。
我写下了几行以返回最大值,最小值,所有值的总和,并在输入五个整数时按升序排列所有值。
在撰写本文时,当我需要放置5个整数时声明一个INT数组时,我错误地写了'num [4]'。 但是,当我使用TDM-GCC 4.9.2 64位版本进行编译时,它可以正常工作。我一意识到并更改为TDM-GCC 4.9.2 32位版本,就没有意识到。
这是我的全部代码;
#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]);
}
}
我对C和各种编程都不熟悉,并且不知道如何搜索这类问题。我知道我在这里这样提问的方式是非常不合适的,我诚挚地向那些对此类提问职位感到恼火的人表示歉意。但这是我的最佳尝试,所以请不要怪,但我愿意接受任何建议或提示。
谢谢。
答案 0 :(得分:3)
在堆栈上进行分配时,面向64位(可能还有Clang)的GCC会将堆栈分配对齐为8个字节。
对于32位目标,将仅使用4个字节的填充。
因此,当您为64位编译程序时,将额外使用四个字节来填充堆栈。这就是为什么当您访问最后一个整数时,它不会出现段错误。
要查看实际效果,我们将创建一个测试文件。
void test_func() {
int n[4];
int b = 11;
for (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
这是32位版本。
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
如果您看完变量声明,可以看到编译器分别生成24个字节和20个字节。
关于您要求的建议/技巧,一个好的出发点是启用所有编译器警告并将其视为错误。在GCC和Clang中,您将使用-Wall -Wextra -Werror -Wfatal-errors
。
但是,如果您使用的是MSVC编译器,我将不建议这样做,因为该编译器通常会发布有关其分发的头文件中的声明的警告。
答案 1 :(得分:3)
通过分析生成的程序集,其他答案涵盖了他实际上可能发生的情况,但真正相关的解释是:在C中,未定义数组的索引是未定义的行为。这就是故事的结尾
UB表示,根据C标准,“允许”执行任何代码。每次运行它都可能做不同的事情。它可以做您想做的事而不会产生不良影响。它可能会做您想要的事情,但是随后某些完全不相关的事情就会以一种有趣的方式表现出来。编译器,操作系统甚至月球相位都会有所不同。是否。
考虑C级别的Undefined Behavior实际发生的情况通常没有用。当然,您可以生成特定编译的程序集输出,并检查其功能,但这是该编译的结果。新的编译可能会改变事情(即使您只是在不同的时间进行新的构建,因为__TIME__
宏的值取决于时间...)。