1

我在面试问题中遇到了第一个问题。但是我想要对这个问题进行适当的解释。我在家里尝试这个,其他一些困惑也随之增加。

#include <stdio.h>
int main()
{
  int arr[4]={10,20,30,40};
  int i;
  for(i=0;i<=4;i++)
  printf("%d,",arr[i]);
  printf("\n");
  return 0;
}

OUTPUT
10,20,30,40,4,

最后一个输出是 4。但它超出了数组索引。我再次认为在内存变量中我存在于数组元素之后。所以我得到了这个答案。

但我再次对此感到困惑

 #include <stdio.h>

int main()
 {
   char arr[4]={10,20,30,40};
   int i;
    for(i=0;i<=4;i++)
       printf("%d,",arr[i]);
    printf("\n");
   return 0;
 }
OUTPUT
10,20,30,40,0,

再次与下面更混淆

#include <stdio.h>
int main()
 {
   int arr[4]={10,20,30,40};
   char i;
   for(i=0;i<=4;i++)
      printf("%d,",arr[i]);
   printf("\n");
   return 0;
}

OUTPUT
10,20,30,40,74743796,

任何人都可以解释为什么这种类型的输出变化?

我使用英特尔 CPU、Ubuntu 操作系统、Gcc 编译器..

如果特定于编译器或特定于体系结构,请在答案中提及。

4

4 回答 4

1

这称为未定义行为。由于您正在访问超出其范围的数组,因此任何事情都可能发生,并且结果不必(也不会)有意义。

于 2012-08-07T07:15:30.713 回答
1

您不应访问超出范围的数组内存。越界数组访问的值可以是任何值 - 它没有预期值并且它具有未定义的行为。似乎您期望在数组之后本地创建的 char 和 int 变量位于数组变量之后的内存中。即使是这种情况,您也没有初始化这些变量,因此它们的值可以是任何值。只是不要访问越界内存,当然也不要试图预测访问它的结果。

于 2012-08-07T07:15:53.647 回答
0

需要明确的是,您所看到的被归类为“未定义的行为”,但您的答案中有足够的信息来了解发生了什么。

首先,我们可以从您关于机器架构的问题中看到一些细节。

关于 CPU

  • CPU是小端

关于操作系统

  • 堆栈向下增长

关于编译器

  • int大小为 32 位
  • char大小为 8 位
  • 局部变量在堆栈上的顺序与它们在代码中的顺序相同
  • 放入char比它需要的更大的块以加快运行时间
  • 使用内存填充来加速代码

所有这些事情结合起来最终得到你所看到的结果。视觉演示最适合展示从这里发生的事情。

第一个案例

在循环结束时,堆栈如下所示:

| 10 | 0 | 0 | 0 | a[0]
| 20 | 0 | 0 | 0 | a[1]
| 30 | 0 | 0 | 0 | a[2]
| 40 | 0 | 0 | 0 | a[3]
|  4 | 0 | 0 | 0 | i (a[4])

的值与应i在的位置相同。a[4]所以a[4]具有与 相同的值i

第三种情况

看起来像什么i取决于很多变量。但似乎这是最有意义的布局。

| 10 | 0 | 0 | 0 | a[0]
| 20 | 0 | 0 | 0 | a[1]
| 30 | 0 | 0 | 0 | a[2]
| 40 | 0 | 0 | 0 | a[3]
|  X | X | X | 4 | i (a[4])

这反映在您看到的打印值中。74743796在小端二进制中是

11110100 01111111 01110100 00000100 

最后一个字节是4. 当您i在代码中引用时,上下文是一个字符,并且只使用最后一个字节。当您引用a[4]时,上下文为 anint并使用所有四个字节。

这很重要,因为它表明编译器将字符推入最后一个字节并用垃圾填充其余部分。

第二种情况

基于其他两个,我希望内存如下

| X | X | X | 10 | a[0]
| X | X | X | 20 | a[1]
| X | X | X | 30 | a[2]
| X | X | X | 40 | a[3]
| 4 | 0 | 0 |  0 | i (a[4])

最后一条记忆线开始发挥作用。当它作为一个访问时int,它被正常处理。但是,当它作为 a 访问时char,编译器假定前三个字节是填充并返回最后一个字节,或者0

于 2012-08-07T08:20:34.457 回答
0

您正在处理的是未定义的行为。

i在第一种情况下,变量恰好arr位于堆栈上的数组之后,因此超出了数组的边界,您正在触摸i变量。i和的类型arr相同 ( ),因此可以将其作为数组的一部分int打印出来。i

在第二种情况下,您试图访问i它,就好像它是一个char变量一样,所以您只能得到它的一部分,它是零。

请记住,编译器可以自由地重新排列堆栈上的变量,因此任何关于i变量应该紧随其后的假设(即使它是按该顺序声明的)都是不正确的。

对于编译器来说,arr它们i只是两个独立的局部变量。的大小arr不保存在内存中的任何位置:只是arr一块具有起始地址的内存。您可以在编译时通过静态sizeof运算符检查其大小。

于 2012-08-07T07:19:50.090 回答