【问题标题】:A C code showing difference between static and dynamic allocation显示静态分配和动态分配之间区别的 C 代码
【发布时间】:2015-08-21 05:49:11
【问题描述】:

我想写一段C代码,看看静态分配和动态分配的区别。

这是我的想法,但它不起作用。

它只是初始化一个大小为10的数组,但分配100元素而不是10个。然后我将初始化另一个足够大的数组,希望替换不属于array1[10]的90个元素,然后我打印出array1100 元素。

int i;    
int array1[10];
int array2[10000];

for(i=0;i<100;i++)
   array1[i] = i;

for(i=0;i<10000;i++)
   array2[i] = i+1;

for(i=0;i<100;i++)
  {
     printf("%d \n",array1[i]);
  }

我希望得到的是在使用静态分配时前 10 个元素之外的垃圾,之后,我将使用 malloc 和 realloc 来确保这 100 个元素正确存在。但很遗憾,似乎内存足够大,以至于剩下的 100 个元素不会被替换!

我尝试在 linux 上运行代码并使用“ulimit”来限制内存大小,但它也不起作用。

有什么想法吗?

【问题讨论】:

    标签: c arrays memory memory-management


    【解决方案1】:

    C实际上并没有对数组进行任何边界检查。这取决于操作系统来确保您访问的是有效内存。

    在数组边界之外访问是未定义的行为,来自c99 draft standard 部分附件 J.2 J.2 未定义的行为包括以下几点:

    一个数组下标超出范围,即使一个对象显然可以用 给定下标(如左值表达式 a[1][7] 给定声明 int a[4][5]) (6.5.6)。

    在此示例中,您要声明一个基于堆栈的数组。越界访问将从已分配的堆栈空间中获取内存。当前未定义的行为对您不利,因为没有 Seg 错误。它是程序员在用 C/C++ 编写代码时处理边界条件的责任。

    【讨论】:

    • 我想说这种行为对你有利。开发人员修复代码后发生崩溃是最好的结果。
    • 我知道,我不是在问为什么它正确打印出 90 个元素:D.... 我只是在问我能做些什么来显示堆栈分配的区域可以被覆盖。
    • @AhmadNourELDeen 请查看Smashing the Stack for fun and profit
    【解决方案2】:

    确实array1的前10个元素之后得到了垃圾。元素 9 之后的所有数据不应被视为由堆栈分配,并且可以随时被覆盖。当程序打印array1 的100 个元素时,您可能会看到for 循环的残余,因为这两个数组彼此相邻分配并且通常没有被覆盖。如果这是在更大的程序中实现的,其他数组可能会占用这两个示例数组之后的空间。

    【讨论】:

    • 我在问题中已经说过了:) ...我应该怎么做才能限制内存大小,甚至强制第二个数组位于 array1 的 10 个分配元素之后?
    • 现在您在搜索问题时遇到了一些问题。当你把你的部分放在一起时,最好确保每个小部分都是正确的,而不是从一个有缺陷的基础开始,然后寻找解决方案。您静态声明 10 个元素的数组,然后当 i = 10 时,您的剩余数据必须转到其他地方。一般来说,你会动态分配 10 个,然后当你达到 10 个时,realloc 是那个数量的两倍并继续......
    • @AhmadNourELDeen 您已经将数组限制为 10 个元素。您无法控制数据在内存中的位置,也永远不需要。简而言之,您只能声明内存的随机部分,从而防止它被其他程序覆盖。
    【解决方案3】:

    当您访问array1[10] 和更高的索引值时,程序将继续写入相邻的内存位置,即使它们不“属于”您的数组。在某些时候,您可能会尝试访问被禁止的内存位置,但只要您正在处理操作系统为您的程序提供的内存,它就会运行。结果将是不可预测的。例如,这可能会损坏属于程序中另一个变量的数据。如果没有其他变量已“正确分配”该内存位置,则当您返回读取它时,您在那里写入的值也可能仍然存在。 (这似乎是您发布的特定案例中发生的情况。)

    话虽如此,我完全不清楚这与静态和动态内存分配之间的潜在差异有何关系,因为您只在程序中进行了静态分配,并且您故意引入了一个错误。

    【讨论】:

    • "之后,我将使用 malloc 和 realloc 来确保这 100 个元素正确存在。"
    • @AhmadNourELDeen 我看到了你在这里引用的那一行,但我无法理解。什么类型的 malloc 调用会给你一些与你在示例中显示的数组对齐的东西?你认为调用 realloc 会发生什么?这在您发布的示例中没有任何明确的上下文,并且很难猜测您的意思,因为到目前为止您所展示的内容有一个明确的错误,该错误在标准中没有明确定义的行为。
    • 我将使用 malloc 保留 40 个字节 (4*10) 然后重新分配 (4*100) 个字节。我想表明动态分配可以让您控制可以从内存中分配的内容。如果我不知道数组的大小但我会从用户那里得到它怎么办?我可以简单地假设使用静态分配的数量很大,但最好使用 realloc。我希望你能明白:)
    • @AhmadNourELDeen 您已经概述了动态分配的案例。毫无疑问,它在运行时更加灵活。目前尚不清楚您如何通过写入静态数组的末尾来比较有关静态和动态分配的任何内容。如果你调用 malloc,你仍然可以写超出你动态分配的数组的末尾,但陷阱基本相同。调用 realloc 将调整数组的大小,但它可能会将其移动到完全不同的位置,因此您会丢失在数组末尾写入的值。最后,一个 int 不一定是 4 个字节。
    【解决方案4】:

    更改内存大小不会解决您的问题,因为当您创建两个数组时,第二个数组应该在内存中的第一个数组之后

    你的代码应该做你认为它会做的事,在我的电脑上,它会做。 这是我的输出: 0 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 ...

    您在什么操作系统上运行您的代码? (我在 linux 64bit 上)。

    无论如何,正如每个人都告诉过你的那样,永远不要在真正的程序中这样做。在数组外部写入是一种未定义的行为,可能会导致你的程序崩溃。

    【讨论】:

    • 是的,我知道 :) ...这只是为了学习目的,而不是真正的程序。但我也在使用 linux 64bit !也许这是随机行为...我会再试一次
    【解决方案5】:

    写出数组的边界不会证明什么,也不是很好定义的。一般来说,调用未定义的行为没有什么聪明或有趣的。你唯一能做到的就是随机崩溃。

    如果您想知道变量分配的位置,您必须查看地址。这是一个例子:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main (void)
    {
      int stack;
      static int data = 1;
      static int bss  = 0;
      int* heap = malloc(sizeof(*heap));
    
      printf("stack: %p\n", (void*)&stack);
      printf(".data: %p\n", (void*)&data);
      printf(".bss:  %p\n", (void*)&bss);
      printf(".heap: %p\n", (void*)heap);
    }
    

    这应该打印 4 个截然不同的地址(.data 和 .bss 可能彼此接近)。要准确了解某个内存区域的起始位置,您需要检查一些链接描述文件或使用系统特定的 API。一旦知道了内存区域的偏移量和大小,就可以确定变量是否存储在不同的内存段之一中。

    【讨论】:

      猜你喜欢
      • 2019-06-18
      • 2012-01-13
      • 2020-12-05
      • 2013-03-17
      • 2020-09-18
      • 2022-11-20
      • 1970-01-01
      • 2015-10-09
      • 2015-05-02
      相关资源
      最近更新 更多