如何确保函数不会获得垃圾指针?

时间:2016-05-31 12:37:25

标签: c++ pointers valgrind

我有一个接受指针并返回enum的函数,具体取决于与该指针相关的一些条件:

my_enum function(char* prt)
{
  /* function body*/
  if (condition1) return enum1;
  if (condition2) return enum2;
  if (condition3) return enum3;
  if (condition4) return enum4;
  else return enum5;
}

我有另一个函数也接受一个指针,调用my_function并对获得的值做出反应:

void another_function(char* ptr)
{
 my_enum result = function(ptr);
 if (result == MY_VALUE) std::cout<<"OK"<<endl;
}

我正在运行Valgrind来检查内存泄漏。上面的代码导致以下错误:

  

条件跳转取决于未初始化的变量。

实际上,可以将未初始化的指针传递给函数 function

我的问题是:处理这种情况的最佳方式是什么(除了使用引用之外)?我不能确保每个将使用该代码的人都会初始化他将传递给函数的指针。如果指针指向一些垃圾(我正在检查它是否为空指针),我无法检查我的函数内部。

我应该忽略这些错误吗?如果它们没用,为什么Valgrind会麻烦告诉我它们呢?必须有我能做的事情。

3 个答案:

答案 0 :(得分:1)

你愿意走多远?如果有人想破坏你的代码,那么他们就会帮助你。

您应用的保护效率越高,就越难获得。

一个简单的方法是检查NULL。这并不能防止愚蠢的指针,但它会阻止那些有意识地失效的指针。大多数人对此表示满意。

然后你可以给指针一个包装类。实例化这个类需要一个有效的对象指向(或者一些无望的跳跃通过箍来给它一个无效的,这相当于有目的地射击你的脚),所以不会出现未初始化指针的情况 - 但是对象可以在它之前停止存在指针被使用。

然后,您可以为这些对象及其指针维护工厂/经理类。每次创建或销毁指针目标对象时,都会创建或使其指针无效。这将是防故障的,除非您的代码是多线程的,并且当您的函数已经超过检查并且在使用验证值之前可能发生破坏。

然后,您可以添加线程安全性,将函数和管理器包装在互斥锁中。这增加了与死锁和同步相关的各种麻烦。但是用户必须非常努力地创建一个派生自您的类(可能首先使用#define private public)来覆盖其安全功能......

每一步都会使你的开销上升到效果真正不再值得努力的水平。所以只需检查指针是否为NULL,并且不要再担心其他人了。

答案 1 :(得分:1)

意见将取决于什么是最好的&#34;方法,因为根本不可能阻止某人传递一个坏的(例如未初始化的,悬空的)指针。

一个常见的解决方案是完全避免使用原始指针,并以一种根本不接受指针的方式编写函数。

一种方法是接受参考。编写代码使其根本不使用原始指针会使用较差的参数调用函数变得更加困难。限制是调用者仍然可以创建一个错误的引用(例如通过解除引用错误的指针),但是为了传递对函数的错误引用而不是传递一个错误的指针需要花费更多精力(或更长的错误序列)坏指针。

另一种方法是按值(或在某些情况下引用)接受一些类对象来保存指针。然后实现所有成员函数,以防止出现错误指针的情况。给该类没有接受指针的成员函数。确保构造函数和其他成员函数保持一致性(正式地,构造函数建立了一组严格的不变量,其他成员函数维护那组不变量)。这包括在尝试使用错误数据构造对象时抛出异常的技术(如果在构造对象的过程中抛出异常,该对象永远不存在,并且不能以任何方式传递给您的函数)。因此,您的函数可以假设 - 如果成功调用 - 它接收的数据是有效的。

问题是,上面的内容使得将不良数据意外传递给您的函数变得更加困难。没有任何技术可以绝对阻止那些有足够决心(无论是通过天才还是愚蠢)的人找到绕过所有安全措施的方法,并将不良数据传递给你的功能。

答案 2 :(得分:0)

基本上有两种解决方案。

  1. 在API的文档中明确指出一个有效的指针和状态。然后任何无效的使用将导致UB,但这不是你的错。但是,处理原始指针是C风格的,并且不受C ++程序员的欢迎。

  2. 获取(引用)一个封装的指针类型,它始终是合理初始化的,例如std::string(而不是const char*),std::unique_ptr或{{1 }}。例如,

    std::shared_ptr

    my_enum function(std::string const&str)
    {
      /* function body*/
      if (str.empty()) // deal with improper input
        std::cerr<<"warning: empty string in function()"<<std::endl;
      if (condition1) return enum1;
      if (condition2) return enum2;
      if (condition3) return enum3;
      if (condition4) return enum4;
      else return enum5;
    }
    

    这避免了原始指针,并且是处理这种情况的C ++方法。后一个代码的一个问题是它只适用于my_enum function(std::unique_ptr<SomeType> const&ptr) { /* function body*/ if (!ptr) { // deal with improper input std::cerr<<"warning: invalid pointer in function()"<<std::endl; return enum_error; } if (condition1) return enum1; if (condition2) return enum2; if (condition3) return enum3; if (condition4) return enum4; else return enum5; } 个参数。可以将其概括为重载(使用SFINAE或其他)来获取(const引用)任何类似对象的自动指针(例如定义为对象unique_ptr,成员obj返回{{1} })。