我真的需要 malloc 吗?

2024-04-14

据我了解,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;DR

如果您不知道自己在做什么,请使用malloc或在所有情况下都是固定大小的数组。 VLA:根本没有必要。请注意,VLA:s 不能是静态的也不能是全局的。

我真的需要使用吗malloc here?

是的。您正在读取一个文件。它们通常比适合 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操作员工作。

虽然我可以理解您可能想使用sizeof有时,我发现很难找到真正需要它的人。毕竟,大小永远不会改变,并且在进行分配时大小是已知的。所以而不是:

int arr[n];
...
for(int i=0; i<sizeof(arr), ...

Just do:

const size_t size = n;
int arr[size];
...
for(int i=0; i<size; ...

VLA:s 不能替代malloc。它们是替代品alloca。如果您不想更改malloc to an 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有一些优点。您可以阅读有关他们的信息here. https://stackoverflow.com/a/42094467/6699433但在我看来,虽然他们有这些优势,但并不值得。每当您发现 VLA:s 有用时,我会说您至少应该考虑切换到另一种语言。

此外,VLA:s 的优点之一(还有alloca)是它们通常比malloc。因此,如果您遇到性能问题,您可能需要切换到alloca代替malloc. A malloc调用涉及向操作系统(或类似的系统)请求一块内存。然后操作系统会搜索该指针,如果找到则返回一个指针。一个alloca另一方面,call 通常只是通过更改单个 cpu 指令中的堆栈指针来实现。

有很多事情需要考虑,但我会避免使用 VLA:s。如果你问我,它们最大的风险是,由于它们很容易使用,人们对它们变得粗心。对于我认为合适的少数情况,我会使用alloca相反,因为这样我就不会隐藏危险。

简短的摘要

  • C11 及更高版本不需要 VLA:s,因此严格来说,您依赖于编译器扩展。然而,对于alloca。因此,如果这是一个非常大的问题,如果您不想使用,请使用固定数组malloc.

  • VLA:s 是语法糖(不是 100% 正确,尤其是在处理多维数组时)alloca并不是malloc。所以不要用它们代替malloc。除了如何sizeof在 VLA 上工作,除了稍微简单的声明之外,它们绝对没有任何好处。

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

  • 您无法检查 VLA 分配是否失败,因此最好提前检查大小是否太大。但是接下来我们会进行错误检查,就像检查是否一样malloc返回 NULL。

  • VLA 不能是全局的也不能是静态的。单独的静态部分可能不会引起任何问题,但如果你想要一个全局数组,那么你不得不使用malloc或固定大小的数组。

这个功能运行良好。

不,不是的。它具有未定义的行为。正如 Jonathan Leffler 在评论中指出的那样,该数组fileName太短了。至少需要 12 个字节才能包含\0-终结者。您可以通过更改为更安全一点:

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

在这种情况下,数组太小的问题将通过创建一个扩展名的文件来体现.bi代替.bin这是一个比当前情况下未定义行为更好的错误。

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

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;
        goto FREE; // Just in case you add another if statement below
                   // and forget to add this
    }

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

如果一个函数总是返回相同的值,那么返回任何东西都是毫无意义的。相反,将其声明为无效。现在我使用返回值使调用者能够检测错误和错误类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我真的需要 malloc 吗? 的相关文章

随机推荐

  • Python:导入模块

    假设我有一个 python 模型fibo py定义如下 Fibonacci numbers module print This is a statement def fib n a b 0 1 while b lt n print b a
  • Wix 安装程序 - 根据属性创建文件夹层次结构

    我在用Wix 3 6 http wixtoolset org 创建一个设置 我仍在不断学习 那里的信息仍然分散 我正在等待我的 Wix 开发人员指南书到达 我目前有一个自定义 UI 对话框 用户可以在其中输入一些应用程序配置 该配置的一部分
  • 创建 IIS 网站的代码

    我需要以编程方式创建一个 IIS 网站 有人可以告诉我执行此操作的代码吗 请不要使用 WMI DirectoryEntry 如果可能的话 当目标是 IIS 7 或更高版本时 有一个API叫做ServerManager http msdn m
  • 为什么创建文件夹后不存在?

    这似乎没有意义 所以我显然做错了什么 DirectoryInfo folder new DirectoryInfo Environment CurrentDirectory Test if folder Exists false folde
  • 当用户向下滚动时显示内容

    您好 请检查此网站 您会看到向下滚动时会自动加载下一个内容 http wallbase net search http wallbase net search 我用谷歌搜索过但找不到它的名字 有例子 教程吗 您可以使用无限滚动 jQuery
  • 添加 NOT LIKE 条件后查询返回的结果太少

    我的 Access 2010 数据库在我稍微更改用户请求的查询时表现得很奇怪 我有一个非常大的表 不要问 查询在其上运 行 然后提供给用户输入表单 不管怎样 今天 一种特殊的形式开始返回的结果比我们预期的要少得多 它通常会返回大约 1200
  • NSURLSession:后台上传然后调用服务api

    我试图使用新的 ios7 后台传输 api 将一些照片上传到服务器 现在发生的是 1 我们将字节上传到s3 2 调用服务api来 完成 上传 我查看了这个文档 似乎背景 NSURLSession 不支持 数据 任务 这是否意味着实际上传完成
  • WCF:在 IErrorHandler 中提供通用的FaultException

    一些上下文 我们有一个自定义 XSD 并使用 WSCF blue 生成 WSDL 和 C 代码 客户端使用ChannelFactory
  • Redux Connect w/ HOC - TypeError:无法设置未定义的属性“props”

    我正在 Next js 中构建快速身份验证高阶组件 但在使用以下代码时遇到了一些问题 import SignIn from components sign in import connect from react redux import
  • iOS - 架构 i386 的 3 个重复符号

    我在理解链接器命令错误时遇到问题 我收到以下信息 duplicate symbol OBJC IVAR XYZAddToDoItemViewController toDoItem in Users Library Developer Xco
  • WindowFromPhysicalPoint 和 WindowFromPoint 之间有什么区别?

    WindowFromPhysicalPoint http msdn microsoft com en us library aa969270 aspx是 Vista 中的新功能 它的文档几乎与WindowFromPoint http msd
  • 如何获取请求Referer路径?

    我需要引用者的路径 我不想要域名 例如 如果引用者是http www google com adsense I want adsense request referer返回一个字符串 但您可以使用Ruby 的 URI 模块 http www
  • Three.js 通过加载模型上的材质名称为材质添加边框

    是否可以在材料周围添加边框 如图所示 我可以通过以下代码设置材质颜色 object traverse function child if child instanceof THREE Mesh child material color se
  • OCUnit 测试在未测试的类中给出错误

    我编写了一个 OCUnit 测试 在 Xcode 3 2 中运行良好 现在在 Xcode 4 中遇到问题 它测试单个静态函数X在班上A 它不调用任何类或函数 库函数之外 唯一需要调用的其他代码是静态构造函数 它初始化两个静态变量 但同样 这
  • init的目的是什么!初始化器失败?

    Apple Swift 编程语言指南提到了 init 的存在 初始化程序 但没有提供任何示例 搜索初始化 在此页面中 https developer apple com library ios documentation swift con
  • 如何保持 Google App Engine 的运行实例

    谷歌应用程序引擎和开发人员控制台似乎又发生了变化 我不知道如何保持谷歌应用程序引擎的实例运行 即使该网站是偶像 应用程序引擎设置下曾经有一个滑块 但现在已经不存在了 我认为在 Google Speak 中我有动态实例 并且我需要一个常驻实例
  • 如何使用 Django 发布和检索 blob

    我有一个blob 这是我使用调整大小的图像
  • 如何在 R 中并排显示列表 - 列表的“cbind”?

    我希望利用我的屏幕空间并排查看几个简单的列表 我并不是想把它们结合起来 a lacbind 但我不介意是否创建一个新的中间结构 当然 要意识到一个列表可能有许多不同类型的对象 尽管我几乎保证我的列表具有相同的结构 如果有必要让事情正常工作
  • 有已知网络爬虫的列表吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试获取网络服务器上某些文件的准确下载数量 我查看了用户代理 其中一些显然是机器人或网络爬虫 但
  • 我真的需要 malloc 吗?

    据我了解 malloc是用来动态分配内存的 在我的代码中 我有时会调用以下函数 int memory get log unsigned char day unsigned char date unsigned char month char