在 C 中使用 malloc
Working of malloc in C
我是 C 的初学者。我想知道,malloc
是如何工作的。
这是一个示例代码,我在尝试理解它的工作原理时写了下来。
代码:
#include<stdio.h>
#include<stdlib.h>
int main() {
int i;
int *array = malloc(sizeof *array);
for (i = 0; i < 5; i++) {
array[i] = i+1;
}
printf("\nArray is: \n");
for (i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
free(array);
return 0;
}
输出:
Array is:
1 2 3 4 5
在上面的程序中,我只为 1 个元素分配了 space,但数组现在包含 5 个元素。因此,由于程序运行顺利且没有任何错误,因此 realloc()
.
的目的是什么?
谁能解释为什么?
提前致谢。
这是典型的 undefined behavior (UB)。
你不能这样写代码。作为一个初学者,认为这是一个错误,一个错误,一个罪恶,一些非常肮脏的东西等等
Could anybody explain why?
如果您需要了解真正 发生了什么(并且细节很复杂),您需要深入了解您的实施细节(您不想这样做)。例如,在 Linux,你可以研究你的 C 标准库、内核、编译器等的源代码。你需要了解编译器生成的机器代码(所以 GCC 用 gcc -S -O1 -fverbose-asm
编译得到一个 .s
汇编文件)。
另见 this(有更多参考)。
尽快阅读 Lattner 在 What Every C programmer should know about undefined behavior 上的博客。每个人都应该看过!
关于 UB 的最糟糕的事情是,可悲的是,有时候,它 出现 到 "work" 就像你想要的那样(但实际上它不是)。
所以尽快学习,系统地避免UB。
顺便说一句,在编译器中启用所有警告可能会有所帮助(但在您的特定情况下可能没有帮助)。如果使用 GCC.
,请养成使用 gcc -Wall -Wextra -g
编译的习惯
请注意,您的程序没有任何数组。 array
变量是一个 指针 (不是数组)所以命名很糟糕。您需要阅读有关指针和 C dynamic memory allocation.
的更多信息
int *array = malloc(sizeof *array); //WRONG
大错特错。名称 array
的选择非常糟糕(它是一个 指针 ,而不是一个数组;您应该花几天时间阅读有什么区别 - "arrays decay into pointers"意思)。您分配一个 sizeof(*array)
与 sizeof(int)
完全相同(通常是 4 个字节,至少在我的机器上是这样)。所以你只为 one int
元素分配 space 。超出此范围的任何访问(即使用任何小的正索引,例如 array[1]
或 array[i]
以及一些正 i
)是 undefined behavior。而且您甚至不针对 malloc
的失败进行测试( 可能 发生)。
如果你想为(假设)8 int
-s 分配内存 space,你应该使用:
int* ptr = malloc(sizeof(int) * 8);
当然你应该检查失败,至少:
if (!ptr) { perror("malloc"); exit(EXIT_FAILURE); };
你需要初始化那个数组(你得到的内存包含不可预测的垃圾),例如
for (int i=0; i<8; i++) ptr[i] = 0;
或者您可以使用
清除所有位(在我知道的所有机器上结果相同)
memset(ptr, 0, sizeof(int)*8);
请注意,即使在这样的 malloc
(或失败的)成功之后,您总是 sizeof(ptr)
是相同的(在我的 Linux/x86-64 框中,它是 8 个字节),因为它是一个 指针 的大小(即使你 malloc
编辑了一个内存区域一百万 int
-s)。
实际上,当您使用 C dynamic memory allocation 时,您需要知道 通常 该指针的 已分配 大小。在上面的代码中,我在 几个 地方使用了 8,这是一种糟糕的风格。至少
会更好
#define MY_ARRAY_LENGTH 8
并在所有地方使用 MY_ARRAY_LENGTH
而不是 8
,从
开始
int* ptr = malloc(MY_ARRAY_LENGTH*sizeof(int));
实际上,分配的内存通常具有运行时定义的大小,您会在某处(在变量、参数等中)保留该大小。
研究一些 现有 自由软件项目的源代码(例如 github),您将学到非常有用的东西。
另请阅读(可能在一两周内)有关 flexible array members 的内容。有时它们非常有用。
程序运行流畅并不代表它是正确的!
尝试将 for 循环中的 5
增加到一定程度(例如,500000
就足够了)。在某些时候,它会停止工作,给你一个 SEGFAULT
.
这称为未定义行为。
valgrind
还会通过类似以下内容警告您有关此问题。
==16812== Invalid write of size 4
==16812== at 0x40065E: main (test.cpp:27)
如果您不知道 valgrind
是什么,请查看:How do I use valgrind to find memory leaks?。 (顺便说一句,这是一个很棒的工具)
这应该有助于为您提供更多说明:
So as the programs runs smoothly without any error
那是因为你运气好。保留 运行 这个程序,你可能很快就会出现段错误。您依赖于 未定义的行为 (UB),这始终是一件坏事™。
What is the purpose of realloc()
?
来自手册页:
void *realloc(void *ptr, size_t size);
The realloc() function changes the size of the memory block pointed to
by ptr to size bytes. The contents will be unchanged in the range
from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added
memory
will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal
to zero,
and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an
earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.
我是 C 的初学者。我想知道,malloc
是如何工作的。
这是一个示例代码,我在尝试理解它的工作原理时写了下来。
代码:
#include<stdio.h>
#include<stdlib.h>
int main() {
int i;
int *array = malloc(sizeof *array);
for (i = 0; i < 5; i++) {
array[i] = i+1;
}
printf("\nArray is: \n");
for (i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
free(array);
return 0;
}
输出:
Array is:
1 2 3 4 5
在上面的程序中,我只为 1 个元素分配了 space,但数组现在包含 5 个元素。因此,由于程序运行顺利且没有任何错误,因此 realloc()
.
谁能解释为什么?
提前致谢。
这是典型的 undefined behavior (UB)。
你不能这样写代码。作为一个初学者,认为这是一个错误,一个错误,一个罪恶,一些非常肮脏的东西等等
Could anybody explain why?
如果您需要了解真正 发生了什么(并且细节很复杂),您需要深入了解您的实施细节(您不想这样做)。例如,在 Linux,你可以研究你的 C 标准库、内核、编译器等的源代码。你需要了解编译器生成的机器代码(所以 GCC 用 gcc -S -O1 -fverbose-asm
编译得到一个 .s
汇编文件)。
另见 this(有更多参考)。
尽快阅读 Lattner 在 What Every C programmer should know about undefined behavior 上的博客。每个人都应该看过!
关于 UB 的最糟糕的事情是,可悲的是,有时候,它 出现 到 "work" 就像你想要的那样(但实际上它不是)。
所以尽快学习,系统地避免UB。
顺便说一句,在编译器中启用所有警告可能会有所帮助(但在您的特定情况下可能没有帮助)。如果使用 GCC.
,请养成使用gcc -Wall -Wextra -g
编译的习惯
请注意,您的程序没有任何数组。 array
变量是一个 指针 (不是数组)所以命名很糟糕。您需要阅读有关指针和 C dynamic memory allocation.
int *array = malloc(sizeof *array); //WRONG
大错特错。名称 array
的选择非常糟糕(它是一个 指针 ,而不是一个数组;您应该花几天时间阅读有什么区别 - "arrays decay into pointers"意思)。您分配一个 sizeof(*array)
与 sizeof(int)
完全相同(通常是 4 个字节,至少在我的机器上是这样)。所以你只为 one int
元素分配 space 。超出此范围的任何访问(即使用任何小的正索引,例如 array[1]
或 array[i]
以及一些正 i
)是 undefined behavior。而且您甚至不针对 malloc
的失败进行测试( 可能 发生)。
如果你想为(假设)8 int
-s 分配内存 space,你应该使用:
int* ptr = malloc(sizeof(int) * 8);
当然你应该检查失败,至少:
if (!ptr) { perror("malloc"); exit(EXIT_FAILURE); };
你需要初始化那个数组(你得到的内存包含不可预测的垃圾),例如
for (int i=0; i<8; i++) ptr[i] = 0;
或者您可以使用
清除所有位(在我知道的所有机器上结果相同)memset(ptr, 0, sizeof(int)*8);
请注意,即使在这样的 malloc
(或失败的)成功之后,您总是 sizeof(ptr)
是相同的(在我的 Linux/x86-64 框中,它是 8 个字节),因为它是一个 指针 的大小(即使你 malloc
编辑了一个内存区域一百万 int
-s)。
实际上,当您使用 C dynamic memory allocation 时,您需要知道 通常 该指针的 已分配 大小。在上面的代码中,我在 几个 地方使用了 8,这是一种糟糕的风格。至少
会更好 #define MY_ARRAY_LENGTH 8
并在所有地方使用 MY_ARRAY_LENGTH
而不是 8
,从
int* ptr = malloc(MY_ARRAY_LENGTH*sizeof(int));
实际上,分配的内存通常具有运行时定义的大小,您会在某处(在变量、参数等中)保留该大小。
研究一些 现有 自由软件项目的源代码(例如 github),您将学到非常有用的东西。
另请阅读(可能在一两周内)有关 flexible array members 的内容。有时它们非常有用。
程序运行流畅并不代表它是正确的!
尝试将 for 循环中的 5
增加到一定程度(例如,500000
就足够了)。在某些时候,它会停止工作,给你一个 SEGFAULT
.
这称为未定义行为。
valgrind
还会通过类似以下内容警告您有关此问题。
==16812== Invalid write of size 4
==16812== at 0x40065E: main (test.cpp:27)
如果您不知道
valgrind
是什么,请查看:How do I use valgrind to find memory leaks?。 (顺便说一句,这是一个很棒的工具)这应该有助于为您提供更多说明:
So as the programs runs smoothly without any error
那是因为你运气好。保留 运行 这个程序,你可能很快就会出现段错误。您依赖于 未定义的行为 (UB),这始终是一件坏事™。
What is the purpose of
realloc()
?
来自手册页:
void *realloc(void *ptr, size_t size);
The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.