我知道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吗?我声明这个数组的方式有什么问题吗?
2条答案
按热度按时间xoefb8l81#
TL;DR
如果你不知道你在做什么,在任何情况下都可以使用
malloc
或固定大小的数组。VLA:s根本不需要。请注意,VLA:s不能是静态的,也不能是全局的。我真的需要在这里使用
malloc
吗?是的。你正在阅读一份文件。它们通常比适合VLA的要大得多。它们应该只用于小型数组。如果有的话。
长版本
我声明这个数组的方式有什么问题吗?
视情况而定。VLA:s作为强制组件从C11中删除,因此严格来说,您正在使用编译器扩展,从而降低了可移植性。将来,VLA:s可能会从编译器中删除(可能性非常低)。也许你还想在不支持VLA:s的编译器上重新编译代码。风险分析取决于你。但我可能会提到
alloca
也是如此。虽然通常可用,但标准并不要求。另一个问题是如果分配失败。如果你正在使用malloc,你有机会从这个问题中恢复过来,但是如果你只打算这样做:
也就是说,只是在失败时退出,而不是试图恢复,那么这并不重要。至少从纯粹的恢复Angular 来看不是这样。
而且,尽管C标准没有强制要求VLA:s必须放在堆栈或堆上,但据我所知,将它们放在堆栈上是很常见的。这意味着由于可用内存不足而导致分配失败的风险要高得多。在Linux上,堆栈通常为8 MB,在Windows上为1 MB。在几乎所有情况下,可用堆都要高得多。声明
char arr[n]
基本上与char *arr = alloca(n)
相同,不同之处在于sizeof
运算符的工作方式。虽然我可以理解您有时可能希望在VLA上使用
sizeof
运算符,但我发现很难找到真实的需要它的地方。毕竟,大小永远不会改变,并且当您进行分配时,大小是已知的。因此,而不是:只要做:
VLA:s不能替代
malloc
。它是alloca
的替代品。如果您不想将malloc
更改为alloca
,则也不应更改为VLA。此外,在许多情况下,VLA似乎是一个好主意,检查大小是否低于一定的限制也是一个好主意,就像这样:
这是可行的,但与此相比:
你并没有让事情变得更容易。它仍然是一个错误检查,所以你真正摆脱的唯一事情是
free
调用。我还可以补充一点,由于VLA的大小太大,VLA分配失败的风险要高得多。因此,如果你知道大小很小,检查是不必要的,但话又说回来,如果你知道它很小,只是使用一个常规的数组,将适合你的需要。但是,我不否认VLA的一些优点。你可以读到他们的here.,但海事组织,虽然他们有这些优势,他们是不值得的。当你发现VLA:s有用的时候,我会说你至少应该考虑切换到另一种语言。
此外,VLA:s(以及
alloca
)的一个优点是它们通常比malloc
更快。因此,如果您有性能问题,您可能希望切换到alloca
而不是malloc
。malloc
调用涉及向操作系统(或类似的东西)请求一块内存。然后操作系统搜索它,并返回一个指针,如果它找到它。另一方面,一个alloca
调用通常只是通过在一条cpu指令中改变堆栈指针来实现的。有很多事情要考虑,但我会避免使用VLA:s。如果你问我,他们最大的风险是,因为他们是如此容易使用,人们变得粗心与他们。对于我认为合适的少数情况,我会使用
alloca
,因为这样我就不会隐藏危险。简短总结
VLA:s在C11和更高版本中是不需要的,所以严格地说,您依赖于编译器扩展。但是,
alloca
也是如此。所以如果这是一个非常大的问题,如果你不想使用malloc
,请使用固定数组。VLA:s是
alloca
的语法糖(不是100%正确,特别是在处理多维数组时),而不是malloc
。所以不要用它们代替malloc
。除了sizeof
在VLA上的工作方式之外,它们除了稍微简单的声明之外,绝对没有任何好处。VLA:s(通常)存储在堆栈上,而malloc完成的分配(通常)存储在堆上,因此大分配失败的风险要高得多。
您无法检查VLA分配是否失败,因此最好提前检查大小是否过大。但是我们有一个错误检查,就像我们检查
malloc
是否返回NULL一样。VLA不能是全局的,也不能是静态的。静态部分本身可能不会引起任何问题,但是如果你想要一个全局数组,那么你必须使用
malloc
或固定大小的数组。此功能工作正常。
不,不是的。它具有未定义的行为。正如Jonathan Leffler在评论中指出的那样,数组
fileName
太短了。它至少需要12个字节才能包含\0
-终止符。您可以通过更改为:在这种情况下,数组太小的问题会通过创建一个扩展名为
.bi
而不是.bin
的文件来表现出来,这是一个比未定义行为更好的bug,这就是当前的情况。你的代码中也没有错误检查。我会这样重写。对于那些认为后藤不好的人来说,好吧,它通常是不好的,但是错误处理既实用又被经验丰富的C程序员普遍接受。另一个常见的用法是打破嵌套循环,但在这里不适用。
如果一个函数总是返回相同的值,那么返回任何值都是没有意义的。将其分解为void。现在我使用返回值使调用者能够检测错误和错误类型。
yvfmudvl2#
首先,行'unsigned char fileData[itemsToRead]'要求堆栈上的内存,如果文件大小很大,这将是一个可怕的错误。你应该使用'malloc'来请求堆上的内存。其次,如果文件大小真的足够大,你应该同意使用病毒内存或动态加载,如'fseek'方法。