理解指向malloc和free的指针
指针在C中是一个非常棘手的事情。因为很多人很难理解它,所以为了更好地理解我写了下面的代码:
#include #include 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
现在,问题和观察:
-
当我为它提供内存之前打印指针时,为什么指针有一个随机值和一个随机地址指向它以及为什么它不是NULL?
-
在我们使用malloc之后,我们可以看到指针指向的地址已更改且其值为NULL,那么malloc真正做到了什么?
-
在我们给它一个值并打印它之后,我们释放它并再次打印它,但是值和地址与数组的后面相同,但不是整数,为什么? 那真正的免费是什么?
-
在我们释放空间之后,我们可以继续更改数组和整数的值,为什么这可以在自由空间之后? 我们不需要重用malloc?
-
因为语言规范是这样说的。 指针的值(即它指向的地址)是不确定的。 它可以指向任何地方,就像
int
可以保存任何值。 读取这些值(就像在第一个printf
使用*p
和*a
)实际上是未定义的行为 。 -
如果你的意思是它指向的数据是
0
,那就是偶然的。 分配的内存不必归零。 例如,它可能是先前使用malloc
分配的块的一部分,然后是free
d(free
不会将内存清零,请参见下面的第3点。) -
那也是偶然的。
free
内存时,它不会被清零,也不必立即使用。 它可以继续存储旧值,直到它被用于其他东西(例如,通过另一个分配) -
这也是未定义的行为 。 你写的是你不再拥有的记忆。 任何事情都可能发生。 该计划可能已经崩溃。 很可能,似乎你可以成功地写入数组,很可能因为内存仍未被其他任何会导致更明显的运行时错误的内容使用。
什么是指针
指针声明看起来很像其他声明:但不要被误导。 声明指针时,开头的关键字声明指针指向的变量类型。 指针本身不属于该类型,它是指向该类型的指针类型。 给定指针仅指向一种特定类型,而不指向所有可能的类型。 实际上,所有指针都被视为整数,但编译器很可能会抱怨不兼容类型之间的赋值。
int *p;
指针p尚未分配一个地址,因此它仍然包含它占用的内存中的任何随机值(无论它在用于p之前的值是多少)。 因此,只要指针保存某些东西的地址,符号* p就等同于直接给出某个东西的名称。 我们从这一切中获得了什么好处? 好吧,它立即绕过函数的按值调用限制。 想象一个函数必须返回,例如,表示该月内一个月和一天的两个整数。
摘要
-
数组总是从故事的零端索引。
-
没有多维数组; 你使用数组的数组。
-
指针指向事物; 指向不同类型的指针本身就是不同的类型。
-
它们彼此之间没有任何共同之处或C中的任何其他类型; 指针和其他类型之间没有自动转换。
-
指针可用于模拟函数的“按引用调用”,但需要做一些工作。
-
向指针递增或添加内容可用于沿着数组步进。 为了通过递增指针来促进数组访问,标准保证在n元素数组中,尽管元素n不存在,但使用其地址不是错误。
指针算术
您不仅可以向指针添加整数值,还可以比较或减去相同类型的两个指针。 它们必须都指向同一个数组,否则结果是未定义的。 两个指针之间的差异被定义为分隔它们的数组元素的数量; 这种差异的类型是实现定义的,将是short,int或long之一。 下一个示例显示了如何计算和使用差异,但在阅读之前,您需要知道一个重点。
在表达式中,数组的名称将转换为指向数组的第一个元素的指针。 唯一不适用的地方是数组名称与sizeof一起使用时,使用字符串初始化数组时,或者数组名称是address-of运算符(一元&)的主题时。 我们还没有看到任何这些案例,将在稍后讨论。 这是一个例子。
#include #include #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是个好主意。
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
分配的malloc
不会被释放。 但是calloc
呢!
3.在我们给它一个值并打印它之后,我们释放它并再次打印它,但是值和地址与数组的后面相同,但不是整数,为什么? 那真正的免费是什么?
free
并不意味着它实际上会删除内存! 它会告诉操作系统我不再需要这个内存,将它用于其他一些进程!
你可以继续使用内存后free(...)
拨打电话free(...)
,没有什么能阻止你。 然而,结果将完全未定义且不可预测。 它只适用于运气。 这是一个常见的编程错误,称为“ 免费使用 ”,它可以在许多程序中使用几年而没有“问题” – 直到它引起问题。
4.在我们释放空间后,我们可以继续更改数组和整数的值,为什么在自由空间之后这可能? 我们不需要重用malloc?
这是完全未定义的行为! 释放内存后,指针仍然指向相同的内存位置。 它被称为悬空指针 。
为避免悬空指针,请在free
后将指针指向null
!
但释放内存后,需要使用内存意味着使用malloc
来分配内存并使用它!