我真的需要malloc吗? [英] Do I really need malloc?

查看:97
本文介绍了我真的需要malloc吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道malloc用于动态分配内存.在我的代码中,我有时会调用以下函数:

I understand that malloc is used to dynamically allocate memory. In my code, I have the following function that I sometimes call:

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吗? 我声明此数组的方式有什么问题吗?

As you may see, the number of items I read from the file can be different each time. The line unsigned char fileData[itemsToRead]; is used to read these variable sized files. I can see that I am allocating memory dynamically in some way. This function works fine. Do I really need to use malloc here? Is there anything wrong with the way I am declaring this array?

推荐答案

我真的需要在这里使用malloc吗?我声明此数组的方式有什么问题吗?

Do I really need to use malloc here? Is there anything wrong with the way I am declaring this array?

这取决于. VLA:s已从C11中删除为必需组件,因此严格来说,您正在使用编译器扩展,从而降低了可移植性.将来,可能会从编译器中删除VLA:s(机会可能极低).也许您还想在不支持VLA:s的情况下在编译器上重新编译代码.有关此问题的风险分析由您决定.

It depends. VLA:s was removed as a mandatory component from C11, so strictly speaking, you are using compiler extensions, thus reducing the portability. In the future, VLA:s might (The chance is probably extremely low) get removed from your compiler. Maybe you also want to recompile the code on a compiler without support for VLA:s. The risk analysis about this is up to you.

另一个问题是分配失败.如果您使用的是malloc,则有机会从中恢复,但是如果您只想执行以下操作:

Another problem is if the allocation fails. If you're using malloc, you have a chance to recover from this, but if you're only going to do something like this:

unsigned char *fileData = malloc(itemsToRead);
if(!fileData)
    exit(EXIT_FAILURE);

也就是说,仅在失败时退出而不尝试恢复,那么这并不重要.至少从纯粹的恢复角度来看并非如此.

That is, just exit on failure and not trying to recover, then it does not really matter. At least not from a pure recovery point of view.

但是,尽管C标准没有强制要求VLA:s必须在堆栈或堆上,但据我所知,将它们放在堆栈上是很常见的.这意味着由于可用内存不足而导致分配失败的风险要高得多.在Linux上,堆栈通常为8MB,在Windows上通常为1MB.在几乎所有情况下,可用堆都高得多.声明char arr[n]char *arr = alloca(n)基本上相同,除了sizeof运算符的工作方式.

But also, although the C standard does not impose any requirement that VLA:s end up on the stack or heap, as far as I know it's pretty common to put them on the stack. This means that the risk of failing the allocation due to insufficient available memory is much, much higher. On Linux, the stack is usually 8MB and on Windows 1MB. In almost all cases, the available heap is much higher. The declaration char arr[n] is basically the same as char *arr = alloca(n) with the exception of how the sizeof operator works.

虽然我可以理解您有时可能希望在VLA上使用sizeof运算符,但我发现很难真正找到它.毕竟,大小永远不会改变,并且在进行分配时就知道大小.因此,代替:

While I can understand that you might want to use the sizeof operator on a VLA sometimes, I find it very hard to find a real need for it. Afterall, the size can never change, and the size is known when you do the allocation. So instead of:

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:s are not a replacement for malloc. They are a replacement for alloca. If you don't want to change a malloc to an alloca, then you should not change to a VLA either.

此外,在许多情况下,VLA似乎是一个好主意,检查大小是否低于一定限制也是一个好主意,例如:

Also, in many situations where a VLA would seem to bee a good idea, it is ALSO a good idea to check if the size is below a certain limit, like this:

int foo(size_t n)
{
    if(n > LIMIT) { /* Handle error */ }
    int arr[n];
    /* Code */
}

那行得通,但可以将其与此进行比较:

That would work, but compare it to this:

int foo(size_t n)
{
    int *arr = malloc(n*sizeof(*arr));
    if(!arr) { /* Handle error */ }
    /* Code */
    free(arr);
}

您并没有真正使事情变得那么容易.它仍然是一个错误检查,因此真正要摆脱的唯一事情是free调用.我还可以补充一点,由于大小太大,VLA分配失败的风险要高得多.因此,如果您知道大小很小,则不需要检查,但是再一次,如果您知道大小很小,只需使用适合您需要的常规数组即可.

You did not really make things that much easier. It's still an error check, so the only thing you really got rid of was the free call. I might also add that it's a MUCH higher risk that a VLA allocation fails due to the size being too big. So if you KNOW that the size is small, the check is not necessary, but then again, if you KNOW that it is small, just use a regular array that will fit what you need.

但是,我不会否认VLA有一些优点:您可以在此处了解有关它们的信息.每当您发现VLA有用时,我会说您至少应该考虑切换到另一种语言.

However, I will not deny that there are some advantages of VLA:s. You can read about them here. But IMO, while they have those advantages they are not worth it. Whenever you find VLA:s useful, I would say that you should at least consider switching to another language.

要考虑很多事情,但是我会避免使用VLA:s.如果您问我,它们的最大风险是由于它们易于使用,因此人们对它们变得粗心.对于我认为适合的少数几种情况,我会改用alloca,因为那样的话我就没有掩盖危险了.

There are many things to consider, but I would avoid using VLA:s. If you ask me, the biggest risk with them is that since they are so easy to use, people become careless with them. For those few cases where I find them suitable, I would use alloca instead, because then I don't hide the dangers.

  • C11和更高版本不需要VLA:s,因此严格来说,您依赖编译器扩展.

  • VLA:s are not required by C11 and later, so strictly speaking, you're relying on compiler extensions.

VLA:s是alloca而不是malloc的语法糖(不是100%正确,尤其是在处理多维数组时).因此,请勿使用它们代替malloc.除了sizeof在VLA上的工作方式之外,它们只提供一点点简单的声明,根本没有任何好处.

VLA:s are syntactic sugar (Not 100% correct, especially when dealing with multidimensional arrays) for alloca and not malloc. So don't use them instead of malloc. With the exception of how sizeof work on a VLA, they offer absolutely no benefit at all except for a somewhat simpler declaration.

VLA:s(通常)存储在堆栈中,而malloc完成的分配(通常)存储在堆中,因此,较大的分配失败的风险要高得多.

VLA:s are (usually) stored on the stack while allocations done by malloc are (usually) stored on the heap, so a big allocation has a much higher risk to fail.

您无法检查VLA分配是否失败,因此最好事先检查尺寸是否太大.但是然后我们有一个错误检查,就像检查malloc是否返回NULL一样.

You cannot check if a VLA allocation failed or not, so it can be a good idea to check if the size is too big in advance. But then we have an error check just as we do with checking if malloc returned NULL.

此功能正常工作.

This function works fine.

不,不是.它具有未定义的行为.正如乔纳森·莱夫勒(Jonathan Leffler)在评论中指出的那样,数组fileName太短.至少需要12个字节才能包含\0终止符.您可以更改为:

No it does not. It has undefined behavior. As pointed out by Jonathan Leffler in comments, the array fileName is too short. It would need to be at least 12 bytes to include the \0-terminator. You can make this a bit safer by changing to:

snprintf(fileName, 
         sizeof(fileName), 
         "%s_%u%u%u%s", 
         "LOG", day, date, month, ".bin");

在这种情况下,数组太小的问题将通过创建扩展名为.bi而不是.bin的文件来体现,这比当前情况下的未定义行为要好.

In this case, the problem with the too small array would manifest itself by creating a file with extension .bi instead of .bin which is a better bug than undefined behavior, which is the current case.

您的代码中也没有错误检查.我会这样重写它.对于那些认为goto不好的人来说,通常是这样,但是错误处理既实用,又在经验丰富的C编码人员中普遍接受.另一个常见用途是打破嵌套循环,但这不适用于此处.

You also have no error checks in your code. I would rewrite it like this. And for those who thinks that goto is bad, well, it usually is, but error handling is both practical and universally accepted among experienced C coders. Another common use is breaking out of nested loops, but that's not applicable here.

int memory_get_log(unsigned char day, unsigned char date, unsigned char month){

    char fileName[12];
    unsigned long readItems, itemsToRead;
    int ret = 0;

    F_FILE *file;

    snprintf(fileName, 
             sizeof(fileName), 
             "%s_%u%u%u%s", "LOG", 
             day, date, month, ".bin");

    file = f_open(fileName , "r");
    if(!file) { 
        ret = 1; 
        goto END;
    }

    itemsToRead = f_filelength( fileName );

    unsigned char *fileData = malloc(itemsToRead);
    if(!fileData) { 
        ret=2;
        goto CLOSE_FILE;
    }

    readItems = f_read(fileData, 1, itemsToRead, file);
    // Maybe not necessary. I don't know. It's up to you.
    if(readItems != itemsToRead) {  
        ret=3;
        goto FREE;
    }

    // Assuming transmit_data have some kind of error check
    if(!transmit_data(fileData, itemsToRead)) {  
        ret=4;
    }

FREE:
    free(fileData);
CLOSE_FILE:
    f_close(file);
END:
    return ret;
}

如果一个函数只返回0,那么返回任何东西都是没有意义的.将其声明为无效.现在,我使用返回值使调用者可以检测到错误和错误类型.

If a function only returns 0, then it's pointless to return anything. Declare it as void instead. Now I used the return value to make it possible for the caller to detect errors and the type of error.

这篇关于我真的需要malloc吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆