如何在C ++中更改指针参数内容

时间:2016-03-23 06:20:05

标签: c++ pointers

给出一个函数声明:

void foo(void *ptr);

我无法更改功能的签名。 现在我需要在foo中分配内存,从ptr开始。 如果我写:

void foo(void *ptr) {
  ptr = malloc(NUM * sizeof(TYPE));
}

当我用它调用时,它实际上并没有改变指针的内容:

void *myPtr = NULL;
foo(myPtr);

我知道我将myPtr的副本传递给foo()的原因。

我需要做的是将输入参数声明为指针的引用:

foo(void *& ptr) {
  ptr = malloc(...);
}

但是,我无法将输入类型从(void *)更改为(void *&)。 我该如何解决这个问题?

我忘记了返回值不是签名的一部分,是的,我无法改变返回。

我认为我不能在C ++中这样做。我必须在此函数之外分配内存。

3 个答案:

答案 0 :(得分:1)

如果不更改功能签名或呼叫站点,则无法实现所需。即一个非常肮脏的黑客将是:

void foo(void *ptr) {
  *((void **)ptr) = malloc(NUM * sizeof(TYPE));
}

int main() {
    void *myPtr = NULL;
    foo(&myPtr);
}

但是,对于大多数情况(特别是,如果您不想更改现有的调用站点),如果编写一个能够执行所需操作的新函数并使foo成为包装器,则可能会更好。对于它(反之亦然,取决于foo之前应该做的事情):

void new_foo (void*& ptr ) {
     // ... whatever came before in the original foo
     ptr = malloc(NUM * sizeof(TYPE));
     // ... whatever came after in the original foo
}

void foo (void* ptr) {//<-- called on old call sites
     void * tptr=ptr;
     new_foo(tptr);
     // free(tptr) ?

}

int main() {
   void* ptr2=NULL;
   foo(ptr1) //<- old call site)

   //...

   void* ptr2=NULL;
   new_foo(ptr2); //<- new call site       
}

答案 1 :(得分:-1)

如果您可以将代码移至/ {和foo,而不更改其签名,这对您的用例是可行的,您可以执行类似的操作...

void underlying_foo(void *ptr)
{
    // Do the stuff.
}

void foo(void *ptr)
{
    ptr = malloc(...);
    underlying_foo(ptr);
}

void new_foo(void **ptr)
{
    *ptr = malloc(...);
    underlying_foo(*ptr);
}

如果你不想/不能用foo扭曲太多,那么你可以使用这样一个丑陋的解决方法......

#include <cstddef>

static void **trap_malloc_trap = nullptr;

void trap_malloc_set(void **trap)
{
    trap_malloc_trap = trap;
}

void trap_malloc_unset()
{
    trap_malloc_trap = nullptr;
}

void *trap_malloc(size_t size)
{
    void *ptr = malloc(size);
    if(trap_malloc_trap != nullptr)
        *trap_malloc_trap = ptr;

    return ptr;
}

void foo(void *ptr)
{
    ptr = trap_malloc(NUM * sizeof(TYPE));
    // Whatever else goes here...
}

则...

#include "foo_header.h"

int main()
{
    void *some_pointer;
    trap_malloc_set(&some_pointer):
    foo(some_pointer);
    trap_malloc_unset();

    return 0;
}

这样,任何调用原始函数的代码仍然可以工作,需要检索指针的新代码可以使用trap_foo()

你可以使用另一个。它更快/更紧,但它会破坏任何依赖现有API的现有代码......

void foo(void *trap_ptr)
{
    void **ptr = (void**)trap_ptr;
    *ptr = malloc(NUM * sizeof(TYPE));
    // Et cetera...
}

则...

void hacky_foo(void **ptr)
{
    foo((void*)ptr);
}

int main()
{
    void *the_pointer;
    hacky_foo(&the_pointer);

    return 0;
}

答案 2 :(得分:-1)

你真的应该把它标记为C问题 - 这种类型的void *指针tomfoolery在C ++中不受欢迎。虽然它是语言的一部分,并且偶尔会有其用途,但您可以更幸运地从寻找C问题的人那里获得所需的专业知识。

继续你的问题,我认为你想要做的事情(至少你发布的例子)根本不可能 - 具体来说:

dnvm upgrade
dnvm restore

考虑一下你在这里做了什么。你声明了一个带有void *类型的变量myPtr,并为它赋予了一个占位符值 - 一个无效的内存区域C程序员调用NULL,它通常为0(C ++程序员更喜欢nullptr,这是一种特定类型,提供一些安全性好处)。

现在你将它传递给foo:

void *myPtr = NULL;
foo(myPtr);

foo收到一个NULL指针 - 一个无效的内存地址,通常为0.当你调用void foo(void *ptr) { ptr = malloc(NUM * sizeof(TYPE)); } 语句时,你的变量ptr = malloc(...)获取malloc返回的内存地址的值 - 为您分配了一些内存,并将指向的地址返回到该内存。

您尚未修改变量myPtr的内容,该变量包含无效的内存地址NULL。

您需要更改函数的签名,以便它可以使用有效地址填充myPtr - 您刚刚分配的内存的地址。你可以这样做:

ptr

我没有看到任何其他方法。顺便说一下,我更喜欢选项1,因为在阅读时它最清楚发生了什么:

 1:  void* foo(void* ptr) { return malloc(...);} // return the address.
 2:  void foo(void** ptr) { *ptr = malloc(...);} // ptr to ptr.
 3:  void foo(void*& ptr) { ptr = malloc(...);} // C++:  reference to ptr.

除非你在foo中对myPtr的内容做任何事情(例如你正在检查它是否为null并且如果没有则分配),你应该丢失参数。