理解指向malloc和free的指针

时间:2014-09-12 06:44:55

标签: c arrays pointers malloc free

指针在C中是一件非常棘手的事情。因为很多人很难理解它,所以为了更好地理解我写了下面的代码:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int *p; // pointer -> will be dynamic allocated
    int *a; // array -> will be dynamic allocated

    // print before allocate memory (1)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
    printf("\n");

    // allocate memory (2)
    p = (int *)malloc(sizeof(int));
    a = (int *)malloc(sizeof(int) * 10);

    // print after allocate, but before give a value to poinetrs (3)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
    printf("\n");

    // give a value to poinetrs (4)
    *p = 1;
    for (int i = 0; i < 10; i++) { a[i] = i; }

    // print after we gave a value to pointers (5)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    // free pointers (6)
    free(p);
    free(a);

    // print pointers after free (7)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    // try to change values after free (8)
    *p = 12;
    for (int i = 0; i < 10; i++) { a[i] = 3; }

    // print after (8)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    return 0;
}

输出:

&p: 0xbfe5db64  p: 0xbfe5dc24   *p: -1075452506
&a: 0xbfe5db68  a: 0xbfe5dc2c   *a: -1075452502

&p: 0xbfe5db64  p: 0x8716008    *p: 0
&a: 0xbfe5db68  a: 0x8716018    *a: 0

&p: 0xbfe5db64  p: 0x8716008    *p: 1
&a: 0xbfe5db68  a: 0x8716018    *a: 0 1 2 3 4 5 6 7 8 9 

&p: 0xbfe5db64  p: 0x8716008    *p: 0
&a: 0xbfe5db68  a: 0x8716018    *a: 0 1 2 3 4 5 6 7 8 9 

&p: 0xbfe5db64  p: 0x8716008    *p: 12
&a: 0xbfe5db68  a: 0x8716018    *a: 3 3 3 3 3 3 3 3 3 3 

现在,问题和观察:

  1. 当我为它提供内存之前打印指针时,为什么指针有一个随机值和一个随机地址指向它以及为什么它不是NULL?

  2. 在我们使用malloc之后,我们可以看到指针指向的地址已更改且其值为NULL,那么malloc真正做了什么?

  3. 在我们给它一个值并打印它之后,我们释放它并再次打印它,但是值和地址与数组的后面相同,但不是整数,为什么?那真正的免费是什么?

  4. 我们释放空间后,我们可以继续更改数组和整数的值,为什么这可以在空闲空间之后?我们不需要重用malloc?

3 个答案:

答案 0 :(得分:5)

  1. 因为语言规范是这样说的。指针的值(即它指向的地址)是不确定的。它可以指向任何地方,就像int可以保存任何值一样。阅读这些值(与第一个*p中的*aprintf一样)实际上是未定义的行为

  2. 如果您指的是0所指的数据,那就是偶然的。分配的内存不必归零。例如,它可能是先前使用malloc分配的块的一部分,然后free d(free不会将内存清零,请参阅下面的第3点。)

  3. 这也是偶然的。当你free内存时,它不会被清零,也不必立即使用。它可以继续存储旧值,直到将其用于其他内容(例如,通过其他分配)

  4. 这也是未定义的行为。你写的是你不再拥有的记忆。任何事情都可能发生。该计划可能已经崩溃。很可能,似乎你可以成功地写入数组,很可能因为内存仍未被其他任何会导致更明显的运行时错误的内容使用。

答案 1 :(得分:3)

1.当我为它提供内存之前打印指针时,为什么指针有一个随机值和一个随机地址指向它以及为什么它不是NULL?

您没有将指针设为NULL。你只是宣布它。声明指针后,它可能有任何值。

使NULL -

int *p = NULL; 
int *a = NULL; 

2.我们使用malloc之后,我们可以看到指针指向的地址发生变化,其值为NULL,那么malloc真正做了什么?

Man Page说 -

void *malloc(size_t size);

malloc()函数分配size个字节并返回指向已分配内存的指针。内存未初始化。如果size为0,那么malloc()将返回NULL或一个以后可以成功传递给free()的唯一指针值。

如果您分配的内存为0意味着只有偶然!由malloc分配的内存未释放。但是calloc会这样做!

3.我们给它一个值并打印后,我们释放它并再次打印,但是值和地址与数组的后面相同,但不是整数,为什么?那真正的免费是什么?

free并不意味着它实际上会删除内存!它将通知操作系统我不再需要这个内存,将其用于其他进程!

你可以在调用free(...)之后继续使用记忆,没有什么可以阻止你。但是,结果将完全未定义且不可预测。它只适用于运气。这是一个常见的编程错误,称为“use after free”,它可以在许多程序中使用几年而没有“问题” - 直到它导致问题。

4.我们释放空间后,我们可以继续更改数组和整数的值,为什么在自由空间之后这可能?我们不需要重用malloc?

这完全是未定义的行为!释放内存后,指针仍然指向相同的内存位置。它被称为Dangling Pointer

为避免悬空指针,请在null之后指向free

但是在释放内存之后,你需要使用内存意味着使用malloc来分配内存并使用它!

答案 2 :(得分:2)

什么是指针

指针声明看起来很像其他声明:但不要被误导。声明指针时,开头的关键字声明指针指向的变量类型。指针本身不属于该类型,它是指向该类型的指针类型。给定指针仅指向一种特定类型,而不指向所有可能的类型。实际上,所有指针都被视为整数,但编译器很可能会抱怨不兼容类型之间的赋值。

int *p;

指针p尚未分配一个地址,因此它仍然包含它占用的内存中的任何随机值(无论它在用于p之前的值是多少)。因此,只要指针保存某些东西的地址,符号* p就等于直接给出某个东西的名称。我们从这一切中获得了什么好处?好吧,它立即绕过函数的按值调用限制。想象一个函数,必须返回两个整数,表示该月内的一个月和一天。

摘要

  • 数组总是从故事的零端索引。

  • 没有多维数组;你改用阵列数组。

  • 指针指向事物;指向不同类型的指针本身就是不同的类型。

  • 他们彼此之间没有任何共同之处或C中的任何其他类型;指针和其他类型之间没有自动转换。

  • 指针可用于模拟“按引用调用”函数,但需要做一些工作。

  • 向指针递增或添加内容可用于沿数组步进。 为了通过递增指针来促进数组访问,标准保证在n元素数组中,尽管元素n不存在,但使用其地址不是错误。

指针算术

您不仅可以为指针添加整数值,还可以比较或减去相同类型的两个指针。它们必须都指向同一个数组,否则结果是未定义的。两个指针之间的差异被定义为分隔它们的数组元素的数量;这种差异的类型是实现定义的,将是short,int或long之一。下一个示例显示了如何计算和使用差异,但在阅读之前,您需要知道一个重点。

在表达式中,数组的名称将转换为指向数组第一个元素的指针。唯一不适用的地方是数组名称与sizeof一起使用时,当一个字符串用于初始化一个数组时,或者当数组名称是address-of运算符(一元&amp;)的主题时。我们还没有看到任何这些案件,他们将在稍后讨论。这是一个例子。

#include <stdio.h>
#include <stdlib.h>
#define ARSZ 10

main(){
  float fa[ARSZ], *fp1, *fp2;

  fp1 = fp2 = fa; /* address of first element */
  while(fp2 != &fa[ARSZ]){
          printf("Difference: %d\n", (int)(fp2-fp1));
          fp2++;
  }
  exit(EXIT_SUCCESS);
}

释放指针

在指针上调用free()不会改变它,只将内存标记为空闲。你的指针仍将指向包含相同值的相同位置,但现在可以随时覆盖该vluae,因此在释放后不应使用指针。确保在释放它之后始终将指针设置为NULL是个好主意。