C ++检查指针是否通过,否则创建一个?

时间:2009-09-23 17:40:11

标签: c++ pointers

好吧,在你有一个带有指针参数并返回指针的函数之前,我已经看到过这种情况。

但是你可以选择不传递一个参数,它会返回一个动态分配的指针,但如果你传递一个指针,那么它只是填充它而不是在堆上创建一个。这是一个没有重载的单一功能。

我的问题是这是如何安全地完成的?

我猜这是这样的:

point* funct(point *p = some_value)
{
 if p == some_value
   //create a point *p on the heap

 else
   //create use the given *p and fill it in

 return p
}

现在我想不出这是否是正确的方法,如果它是什么可能是一个好的some_value?它不能为NULL,因为当你传递空指针时它也会是NULL并且不确定它是否可以安全地使它大于0.你也不能在指针上有负数,所以什么是安全值?

有什么好方法可以跨平台进行PORTABLE吗?

编辑:

好吧也许我没有正确解释基本上我想要这样使用的功能:

point *x;
point *y;

x = func();
func(y);

x = func(NULL);

如果我使用NULL,只有当我执行func(y);

时才会出现错误分段错误

原因是:

用户传递他管理的指针,例如在堆栈上创建的指针,或者如果没有给出,则函数将返回动态指针。我不想强制只返回动态内存或仅接受指向填充的内容。

我知道我以前见过这件事。

13 个答案:

答案 0 :(得分:8)

正常的解决方案是'if NULL allocate' - 这就是'c'realloc的作用。

看起来你想拥有一个使用现有内存的函数(如果提供),或者分配它自己的内存。 如果传递有效指针,则不清楚应该返回什么 它应该返回指针吗?
或者它应该为null以确保没有两个指向同一位内存的指针副本 - 如果它们被释放将导致问题。

将**指针传递给函数并需要参数可能更安全 - 即使该争论是指向null的指针。

答案 1 :(得分:3)

很明显,如果有人打电话给函数(NULL),那么无论如何都会导致崩溃。那么为什么不让some_value = NULL来制作

if(p==NULL){
  p=dynamic_memory;
}

其中dynamic_memory在构造函数中分配(不是线程安全的!)或者通过调用new

替换dynamic_memory

编辑:

或者。如果你的函数中必须有3个状态,一个如果没有提供参数,一个用于传递有效指针,一个用于传递NULL,那么你可以使用指向指针。

void *func(void** p=NULL){
  if(p==NULL) ...//user supplied no argument
  if(*p==NULL) ...//user supplied NULL
  else //user supplied valid pointer

这似乎不是你想要的,然后人们将不得不通过'&'传递指针你的函数..(&NULL甚至有效吗?)

答案 2 :(得分:3)

我的猜测更多的是:

point* funct(point *p = NULL)
{
    if (p == NULL) {
        // create a point *p on the heap
        // and use it
        return(p);
    }
    else {
        //use the given *p and fill it in
        return(NULL);
    }
 }

不太确定是否有可能指向你的点对象传入。可能很难检查,并检查传入的对象的类型,即使用RTTI“查看封面”,并不是最好的面向对象实践。

答案 3 :(得分:2)

类似的东西:

T* func(T* p) {
    if(!p) {
        p = new T;
    }
    // do whatever with p
    return p;
}

然后你可以这样做:

T* x = func(NULL);
// whatever
delete x;

或:

T x;
func(&x);

修改

有几个库使用的非线程安全选项。基本上它的工作原理如下:

T* func(T* p) {
    static T buf;
    if(!p) {
        p = buf;
    }
    // do whatever with p
    return p;
}

然后你可以这样做:

T* p = func(NULL);

或:

T x;
T* p = func(&x);

这些版本通常还有“可重入”版本,它们通常与非线程安全版本相关联:

T* func(T* p) {
    // behaves as above example, except now we can
    // use func_r in a thread safe way if we need to
    static T buf;
    return func_r(p, buf);
}

T* func_r(T* p, T *buf) {
    if(!p) {
        p = buf;
    }
    // do whatever with p
    return p;
}

答案 4 :(得分:2)

调用func(y)时出错,因为y未初始化。它包含指向内存中随机位置的随机位。您需要将y初始化为NULL。

 point *x, *y;

 x = func();
 y = NULL;
 y = func(y); // so it can be deleted later you need to assign the return value
 delete x;
 delete y;

为什么不这样做?并完全避免堆分配。

 point x, y;
 func(&x);
 func(&y);

或者:

 point *x;
 point *y = new point();
 x = func();
 func(y);
 delete x;
 delete y;

正如我在上面的评论中所说,内存所有权在您的功能中令人困惑。动态分配其结果的函数应该每次或不执行任何操作。当他们在某些时候这样做时,内存泄漏的可能性要高得多。

就个人而言,我会更进一步,避免所有分配,通过引用传递:

 void func(point& p)
 {
      //do stuff
 }

 point x, y;
 func(x);
 func(y);

答案 5 :(得分:1)

如果要分配,则应传递NULL(默认值为null),如果要填写则传递空指针。

正如jeffamaphone评论的那样,空指针和NULL之间存在差异,使用条件语句检查它是否为空指针或NULL

答案 6 :(得分:1)

使用NULL有什么问题?这是处理这个问题的一般方法。如果确实需要区分“调用者没有通过”和“调用者传递NULL”,那么在32位系统上使用0xFFFFFFFF或在64位上使用0xFFFFFFFFFFFFFFFF系统

point * funct(point * p = (point *)(-1))
{
    if (p == (point *)(-1))
    {
        p = new point();
    }

    if (p == NULL)
    {
        // special case handling
        return NULL;
    }

    // fill in p
    return p
}

只要你在二元赞美架构上,' - 1'演员将始终是最大指针值。如果您愿意,可以随意使用重新演绎的演员来代替C风格演员。

答案 7 :(得分:1)

  

但是你可以选择不传递一个参数,它会返回一个动态分配的指针,但如果你传递一个指针,那么它只是填充它而不是在堆上创建一个。这是一个没有重载的单一功能。

您可以重载函数,而不是使用默认参数:

point* funct(point *p = some_value)
{
    // fills p
}

point* funct()
{
    return funct(new point());
}
  

它可能不是正确的方法,但在某些地方我认为在linux库中有一个功能与上面完全一样,不确定它是如何在内部分配的

我不知道有任何这样的功能。我猜你会想到realloc接受指针和size_t,然后决定是分配内存,调整已分配的内存还是释放内存。

  

现在我想不出这是否是正确的方法,如果它是什么可能是一个好的some_value?它不能为NULL,因为当你传递空指针时它也会是NULL并且不确定它是否可以安全地使它大于0.你也不能在指针上有负数,所以什么是安全值?

我认为你的困惑来自对NULL指针的基本误解。

观察:

int x = 5;
int* px = &x;
int* p_null = NULL;
int* p;
int* p_new = new int();

px指向x,因此它不是NULLp_nullNULL开头,这意味着它没有任何意义。

我认为您使用术语“空指针”来引用pp_new之类的内容。但是,尽管p未指向有效对象,但它也未设置为NULL。它实际上是一个无效的指针,并且没有可移植的方式来判断它是无效的(在某些机器上它是may be possible to tell if something is obviously not valid,但即便如此,它也无法捕获所有无效指针 - 参见注释 - 和you probably don't want to anyway - 下半场。)

p_new指向动态内存中的有效地址。它不是NULL。它不是空的。实际上,new会将int初始化为0。

换句话说,NULL是您传递给函数的值,这些函数期望指针告诉它们您没有指针。就是这样。并没有真正的空指针的想法。悬空指针(未初始化的指针或指向您无权访问的内存的指针)不是NULL,因为如果它们是NULL则不会悬挂。并且在所有情况下都无法验证指针以确定它们是否有效。


注意

考虑:

int* p_new2 = new int();
delete p_new2;

delete未将p_new2设置为NULL。所以在delete之后,p_new2将在有效指针的正确范围内有一个地址(意味着Windows的VirtualQuery方法会说“确定,指针可以指向那里”),但不会允许实际取消引用该内存地址。

注2

这是一个非常糟糕的主意,不要这样做:

int* funct()
{
    int y = 5;
    return &y;
}

int* x = funct();
y返回后

funct()不再存在。所以指向y的{​​{1}}指针指向不存在的东西。所以你得到一个悬垂的指针。你不是在谈论这样做,但这是一个常见的错误,它会咬你。

答案 8 :(得分:1)

你必须问自己为什么传递NULL我们给你一个seg-fault。当然不是因为NULL不是一个合适的值,它将由你的代码在传递NULL时所做的任何事情引起。但是,您选择不显示该代码。

您是否在调试器中完成了此代码?

除此之外,在C ++ 不使用NULL。这是一个宏,并且对不正确的重新定义持开放态度。使用普通零(0)。语言放置保证转换为指针时的零文字常量不是有效地址。很可能你的NULL宏实际上被定义为零。

如果您尝试取消引用空指针,则会出现错误。

答案 9 :(得分:0)

默认值是它的默认值。这里没有 not 传递值这样的东西。它可能只发生在可变参数计数上,但这是不同的故事。

什么是“空指针”?

答案 10 :(得分:0)

你在找这个:

point* funct(point *p = some_value)
{
 if (p == NULL)
   return NULL;

 if (p == some_value) 
   p = new point;

   return p;
}

答案 11 :(得分:0)

好吧我已经决定不使用那个习惯看起来很糟糕虽然我知道其中一个unix或linux系统库有这样的功能。

我没有回答我自己的问题,但我只记得ctime成语,这可能是一种更安全的方式来做我想要的。我在谈论time()函数。

指针或按值返回对象的位置。

我认为它甚至可以提供更好的灵活性,通过用户控制的指针返回值,堆或堆栈分配。我不知道为什么我之前没想过这个。

你们有什么事,ctime成语好多了吧?

编辑:那个成语是否使用NULL检查?

答案 12 :(得分:0)

如果您真的希望以这种方式拆分它们,您不会意外地提供默认功能,那么您应该使功能过载。

point* funct() {
    //alocate a point* p here
    return funct(p);
}

point* funct(point *p) {
    //do stuff
}