【问题标题】:How can I make sure that a caller passes a malloc'd pointer?如何确保调用者传递了 malloc 的指针?
【发布时间】:2011-03-20 03:10:09
【问题描述】:

我有一个函数,它将作为参数给出的指针重新分配给新大小。现在,问题是 - 根据手册页 - realloc 需要一个由 malloc 或 calloc 之前返回的指针。

如何确保调用者传递的指针满足这些要求? 似乎没有内置的 C 机制(如类型限定符或其他东西)可以这样做。

现在,在我重新构建我的 API 之前(因为我认为现在的功能不够健壮) - 你能确认我没有遗漏什么吗?

提前致谢。

编辑: 一种解决方案显然是 malloc in 函数。问题在于调用者没有“看到”分配。因此,我需要在文档中明确说明他必须释放指针。这比期望他们提供一个 malloc 的指针(这意味着调用者必须释放它)更糟糕。

我真正想要的是在编译时阻止滥用的东西。那,还有一匹小马。 ;-)

【问题讨论】:

  • 你需要让它真的那么简单吗?
  • 至少我愿意,是的
  • 既然你做不到,你可能应该停止担心,做一些更有趣的事情。人们可以颠覆您愿意尝试的任何和所有检查或预防措施。不要浪费时间预防。花时间大声而清晰地崩溃,这样他们就可以阅读错误消息并意识到自己的错误。

标签: c realloc


【解决方案1】:

如何确保调用者传递的指针满足这些要求?

文档。

定义 API。

如果编写调用者的人拒绝遵循 API,事情就会崩溃。他们拒绝遵守你的规则,事情就崩溃了。他们期待什么?

【讨论】:

    【解决方案2】:

    归根结底,C 中的指针只不过是一个机器字,它可能指向也可能不指向进程内存中的重要数据,这些数据可能由 malloc 分配,也可能不分配。任何指针都没有这样的信息。

    只需向 API 添加一个又大又胖的警告。如果文档清楚地说“永远不要传递这个不是 malloc'd 的函数指针”,并且有人仍然这样做并得到错误/崩溃,那不是你的错 - 你无法在这里进行防御性编程。

    【讨论】:

      【解决方案3】:

      您无法确定指针指向的区域是否已被[m|c]alloc();'d。您只能定义您的 API,并希望您的 API 用户遵循它。

      自己(被调用者)分配内存是否禁止?如果您受此约束,您仍然可以使用 realloc(NULL, size); 进行初始分配。

      更多关于你的问题的信息会很好。

      【讨论】:

        【解决方案4】:

        一种可能的方法。 把它抽象出来。

        foo.c
        #include "foo.h"
        struct foo{
           ...
        } ;
        
        foo *newFoo(...)          
        void resize(foo *f)      
        
        foo.h
        
        struct foo;
        typedef struct foo;
        
        caller.c
        
        foo *myfoo;
        myfoo = newFoo(..)
        resize(myfoo);
        

        【讨论】:

          【解决方案5】:

          用户应该如何首先获得这个 malloc'ed 指针?是不是有可能是你的API中的另一个函数获得的?否则,这对我来说听起来像是样板代码,在调用你的函数之前必须使用 malloc 分配一块内存。

          在您的情况下,我会尝试这样做。让调用者通过我提供的函数获取有效指针并通过另一个函数释放它。更好的是,将它们全部包裹在一个不透明的结构中。

          my_param_type my_param = init_param(....);
          ...
          my_function_that_wants_malloc(.....,my_param);
          ...
          free_param(my_param);
          

          这在您的 API 中有意义吗?

          拥有自己的类型,即使是最蹩脚的用户也很清楚,他必须通过预期的 init_param() 函数来获取它。

          【讨论】:

            【解决方案6】:

            更改您的实现,以免使用 realloc。如果传入的缓冲区太小,只需 malloc 一个新的更大的缓冲区并将传入缓冲区的内容 memcpy 到它。这将提高 API 的可用性,但会降低性能。

            【讨论】:

            • 那更糟。现在您必须依靠调用者来释放()新缓冲区。如果他们不这样做,它就不会大声崩溃;它只会泄漏内存。如果您想防范不阅读 API 文档的用户,请不要以会静默失败的方式进行操作。
            • @Nathon:如果您在 API 调用中重新分配指针,这可能比分配新内存更糟糕。原始指针现在可能无效,但如果程序员没有阅读 API 文档,他可能不知道这一点并决定向其写入更多数据。这很容易导致微妙的数据损坏错误,例如,如果旧指针现在部分通过新的不同缓冲区。
            【解决方案7】:

            如果您只有指针值,则没有(标准)方法可以确定该指针值是否由 malloc() 返回。如果您对动态内存分配在您的平台上的工作方式有深入的了解,那么您可以根据指针值做出有根据的猜测,但即使这样也不能保证您看到的东西实际上是来自 @987654322 的返回值@ 而不是从那个偏移的值(例如,*ptr = malloc(10); foo(&ptr[5]);)。

            一种可能值得也可能不值得努力的策略(IME,它不是)是将malloc()、realloc() 和free() 隐藏在某种内存管理器后面,该内存管理器跟踪分配的指针和尺寸。您的代码不会调用realloc(),而是调用内存管理器中的resize 函数,如果传递给它的指针不在托管指针列表中,它将返回错误代码。

            【讨论】:

              【解决方案8】:

              您可以在函数中维护一个静态指针并自己调用 realloc。返回静态指针。这样用户就不用担心分配或释放。当进程终止时,变量将被自动释放。

              请注意,如果 realloc 失败,它会返回一个空指针,但原始 malloc 的内存保持不变,这是内存泄漏的常见来源。

              【讨论】:

              • 这没有回答问题。
              • 不,但这是一个可能适用于任何答案的重要说明,因此即使我更愿意说“if (tmp) a=tmp; else {note the failure but保持原样}”。如果重新分配失败,任何人都不应该尝试使用分配空间的“扩展”区域,但之前拥有指针的人应该是删除它的人。
              【解决方案9】:

              创建一个重命名的 void 指针的自定义数据类型。

              typedef void* myPointerType;
              

              编写用户必须用来分配内存的分配函数(可以是malloc 的简单包装器)。

              myPointerType myAllocateFunction (size_t size) {
                  return (myPointerType)(malloc(size));
              }
              

              您还想创建一个匹配的“免费”功能。

              现在,让您的函数(执行realloc 的函数)将myPointerType 对象作为参数而不是通用指针。您可以将其转换回void* 以与realloc 一起使用。为了解决这个问题,用户必须手动将非malloced 指针转换为myPointerType,但您可以在文档中指定不允许与myPointerType 进行转换(所以如果他们这样做并且他们的应用程序崩溃了,这是因为他们滥用了 API)。

              还有更强大的方法可以强制执行此操作,但我不确定这是否值得麻烦。你不能完全避免一个 API 的傻瓜(你会惊讶于这些天傻瓜的能力)。无论您最终实现什么,确保正确使用您的 API 的最佳方法是提供清晰、全面的文档。

              小马只能靠你自己了,抱歉。

              【讨论】:

              • 在构建为静态/共享库时,该 typedef 是否进入 *.h 文件?抱歉新手问题。
              • @TristonJ.Taylor- 如果这是您共享库的公共接口的一部分,那么是的,它需要放在头文件中。否则,您将拥有引用尚未定义的类型的函数,并且您的用户将收到编译时错误。
              【解决方案10】:

              如果您正在寻找编译时检查,您唯一能做的就是声明一些由“已批准”分配调用返回的新类型。我会建议类似:

              typedef struct MEM_INFO {void *ptr;整数分配;结构 *privateMemInfo priv;} ALLOCMEM[0];

              privateMemInfo 将在您的 .c 文件中定义,而不是在标头中,从而在一定程度上保护其中的字段免受恶作剧。请注意,由于 ALLOCMEM 将是一个数组类型,因此 ptr 和 allocsize 将始终使用箭头运算符而不是点来访问。将 ALLOCMEM 作为参数的例程自然会得到一个指向 ALLOCMEM 的指针,允许它“很好地”执行重定位。因此,例如,人们会使用:

              ALLOCMEM foo = {0}; if (reallocate(foo,12345) >= 0) /* 成功了 */

              【讨论】:

              • 好主意,但是:typedef char * strptr;打败你! strptr 可以转换为 char *,CPP 将在编译/设计时捕获错误。如果有人试图颠覆这一点,哦,是的,realloc 将自己大声失败。好点子。谢谢。我认为这是我自己和操作人员真正想要的。
              • 我的声明是错误的,因为它需要是 ALLOCMEM[1],但基本思想是所有分配例程都需要对“struct MEM_INFO”的引用。虽然没有什么可以阻止代码弄乱“struct MEM_INFO”的内容,但编译时检查将确保只有对此类结构的引用才能传递给分配/释放例程。
              【解决方案11】:

              因此我需要明确地说 他必须释放的文档 指针。这甚至比 期望他们提供一个malloc'd 指针(这意味着 调用者必须释放它)。

              在 C 库中这是一种常见的做法:

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-03-08
                • 2019-03-30
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多