本地数组和数组索引变量如何存在于内存中?

时间:2012-08-07 07:10:53

标签: c arrays memory

我在面试问题中遇到了第一个问题。但我想对这个问题做一个正确的解释。我在家里尝试这个,其他一些混乱也在上升。

#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,

任何人都可以解释为什么输出会出现这种变化?

我使用的是intel cpu,Ubuntu os,Gcc complier ..

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

4 个答案:

答案 0 :(得分:1)

这称为Undefined Behavior。由于您正在访问其范围之外的数组,因此任何都可能发生,并且结果不必(也不会有意义)。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

在第一种情况下,恰好将i变量放在堆栈上的arr数组之后,因此超出了触及{{1}的数组的边界变量。 ii的类型相同(arr),因此将int打印出来就好像它是数组的一部分一样正常。

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

请记住,编译器可以自由地重新排列堆栈上的变量,因此任何假设char变量应该紧跟在之后(即使按照该顺序声明)是不正确的。

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

答案 3 :(得分: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时,上下文将作为char并且仅使用最后一个字节。当您引用a[4]时,上下文为int,并且使用了所有四个字节。

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

第二个案例

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

| 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访问时,它会正常处理。但是,当它作为char访问时,编译器假定前三个字节是填充并返回最后一个字节,或0