我知道malloc是用来动态分配内存的。在我的代码中,我有以下函数,我有时会调用它:
int memory_get_log(unsigned char day, unsigned char date, unsigned char month){
char fileName[11];
unsigned long readItems, itemsToRead;
F_FILE *file;
sprintf(fileName, "%s_%u%u%u%s", "LOG", day, date, month, ".bin");
file = f_open(fileName , "r");
itemsToRead = f_filelength( fileName );
//unsigned char *fileData = (unsigned char *) malloc(itemsToRead);
unsigned char fileData[itemsToRead]; //here I am not using malloc
readItems = f_read(fileData, 1, itemsToRead, file);
transmit_data(fileData, itemsToRead);
f_close(file);
return 0;
}
如您所见,我每次从文件中读取的项目数可能不同。该行unsigned char fileData[itemsToRead];
用于读取这些可变大小的文件。我可以看到我正在以某种方式动态分配内存。此函数工作正常。我真的需要在这里使用 malloc 吗? 我声明此数组的方式有什么问题吗?
TL;博士
如果您不知道自己在做什么,请在所有情况下使用malloc
或固定大小的数组。VLA:s根本不是必需的。请注意,VLA:s 既不能是静态的,也不能是全局的。
我真的需要在这里使用
malloc
吗?
是的。您正在读取文件。它们通常比适合VLA的大得多。它们只应用于小型阵列。如果有的话。
长版本
我声明此数组的方式有什么问题吗?
这要看情况。VLA:s 作为必需组件从 C11 中删除,因此严格来说,您使用的是编译器扩展,从而降低了可移植性。将来,VLA:s可能会(可能性极低)从编译器中删除。也许您还想在不支持 VLA:s 的编译器上重新编译代码。这方面的风险分析取决于您。但我可能会提到,alloca
也是如此。虽然通常可用,但标准并不要求。
另一个问题是分配失败。如果您使用的是 malloc,则有机会从中恢复,但如果您只打算执行以下操作:
unsigned char *fileData = malloc(itemsToRead);
if(!fileData)
exit(EXIT_FAILURE);
也就是说,只是在失败时退出而不试图恢复,那么这并不重要。至少从纯粹的恢复角度来看不是这样。
而且,尽管 C 标准没有要求 VLA:s 最终出现在堆栈或堆上,但据我所知,将它们放在堆栈上是很常见的。这意味着由于可用内存不足而导致分配失败的风险要高得多。在Linux上,堆栈通常为8MB,在Windows上为1MB。在几乎所有情况下,可用堆都要高得多。声明char arr[n]
与char *arr = alloca(n)
基本相同,只是sizeof
运算符的工作方式不同。
虽然我可以理解您有时可能想在 VLA 上使用sizeof
运算符,但我发现很难找到对它的真正需求。毕竟,大小永远不会改变,并且在进行分配时就知道大小。所以代替:
int arr[n];
...
for(int i=0; i<sizeof(arr), ...
只需做:
const size_t size = n;
int arr[size];
...
for(int i=0; i<size; ...
VLA:s 不能替代malloc
。它们是alloca
的替代品.如果您不想将malloc
更改为alloca
,则也不应更改为VLA。
此外,在许多情况下,VLA 似乎是一个好主意,检查大小是否低于某个限制也是一个好主意,如下所示:
int foo(size_t n)
{
if(n > LIMIT) { /* Handle error */ }
int arr[n];
/* Code */
}
这将有效,但将其与此进行比较:
int foo(size_t n)
{
int *arr = malloc(n*sizeof(*arr));
if(!arr) { /* Handle error */ }
/* Code */
free(arr);
}
你并没有真正让事情变得那么容易。它仍然是一个错误检查,所以你唯一真正摆脱的是free
调用。我还可以补充一点,由于尺寸太大,VLA 分配失败的风险要高得多。因此,如果您知道大小很小,则不需要检查,但是话又说回来,如果您知道它很小,只需使用适合您需要的常规数组即可。
但是,我不会否认VLA:s有一些优点。您可以在此处阅读有关它们的信息。但是IMO,虽然他们有这些优势,但不值得。每当你发现VLA:s有用时,我会说你至少应该考虑切换到另一种语言。
此外,VLA:s(以及alloca
)的一个优点是它们通常比malloc
快。因此,如果您遇到性能问题,则可能需要切换到alloca
而不是malloc
。malloc
调用涉及向操作系统(或类似的东西)请求一段内存。然后,操作系统会搜索该指针,如果找到它,则返回一个指针。另一方面,alloca
调用通常只是通过更改单个 CPU 指令中的堆栈指针来实现的。
有很多事情需要考虑,但我会避免使用 VLA:s。如果你问我,它们最大的风险是因为它们非常易于使用,人们对它们变得粗心大意。对于我认为合适的少数情况,我会用alloca
代替,因为那样我就不会隐藏危险。
简短摘要
C11 及更高版本不需要 VLA:s,因此严格来说,您依赖于编译器扩展。但是,
alloca
也是如此。因此,如果这是一个非常大的问题,如果您不想使用malloc
,请使用固定数组。VLA:s 是用于
alloca
而不是malloc
的语法糖(不是 100% 正确,尤其是在处理多维数组时)。所以不要使用它们代替malloc
.除了sizeof
如何在VLA上工作之外,除了更简单的声明之外,它们绝对没有任何好处。VLA:s(通常)存储在堆栈上,而malloc完成的分配(通常)存储在堆上,因此大型分配失败的风险要高得多。
您无法检查 VLA 分配是否失败,因此最好提前检查大小是否太大。但是,我们有一个错误检查,就像我们检查
malloc
是否返回 NULL 一样。VLA 既不能是全局的,也不能是静态的。单独的静态部分可能不会引起任何问题,但是如果你想要一个全局数组,那么你被迫使用
malloc
或固定大小的数组。
此函数工作正常。
不,它没有。它具有未定义的行为。正如Jonathan Leffler在评论中指出的那样,数组fileName
太短。它至少需要 12 个字节才能包含