代码之家  ›  专栏  ›  技术社区  ›  user2738748

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

  •  -1
  • user2738748  · 技术社区  · 9 年前

    我有一个接受指针并返回 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以检查内存泄漏。上述代码导致以下错误:

    条件跳转依赖于未初始化的变量。

    事实上,可以向函数传递未初始化的指针 作用 .

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

    我应该忽略这些错误吗?如果它们是无用的,为什么Valgrind会费心通知我呢?我一定能做点什么。

    3 回复  |  直到 9 年前
        1
  •  1
  •   SF.    9 年前

    你愿意走多远?如果有人想破坏你的代码,他们会的,你也无能为力。

    您应用的保护越有效,它们就越难实现。

    一个简单的方法是检查NULL。这并不能防止愚蠢的指针,但可以防止有意识地使其无效。大多数人对此感到满意。

    然后您可以给指针一个包装类。实例化这个类需要一个指向的有效对象(或者一些毫无希望的跳过环来给它一个无效的对象,这相当于故意放屁),因此不会出现未初始化指针的情况,但对象可以在使用其指针之前停止存在。

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

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

    每走一步,你的头顶就会爬到一个水平,而这个水平的效果真的不值得你付出努力了。所以,只需检查该指针是否为NULL,不要担心其他人会把你弄出来。

        2
  •  1
  •   Peter    9 年前

    对于什么是“最佳”方法,人们的看法会有所不同,因为根本不可能阻止某人传递一个坏的(例如未初始化的、悬空的)指针。

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

    一种方法是接受推荐信。编写代码,使其根本不使用原始指针,这样就很难用错误的参数调用函数。限制是,调用方仍然可以创建错误引用(例如,通过取消对错误指针的引用),但将错误引用传递给函数要比传递错误指针花费更多的精力(如果无意中这样做的话,错误序列会更长)。

    另一种方法是通过值(或引用,在某些情况下)接受某个类对象来保存指针。然后实施 全部的 它的成员函数,以防止出现持有错误指针的情况。不给该类提供接受指针的成员函数。确保构造函数和其他成员函数保持一致性(在形式上,构造函数建立一组严格的不变量,其他成员函数维护这组不变量)。这包括在尝试使用错误数据构造对象时引发异常等技术(如果在构造对象的过程中引发异常,则该对象永远不存在,并且无法以任何方式传递给函数)。因此,如果函数被成功调用,则可以假定它接收的数据是有效的。

    问题是,上述情况使得意外地将错误数据传递给函数变得更加困难。任何技术都无法完全阻止那些决心足够坚定(无论是天才还是愚蠢)的人找到绕过所有安全措施的方法,并将不良数据传递给您的功能。

        3
  •  0
  •   Walter    9 年前

    基本上有两种解决方案。

    1. 期望一个有效的指针,并在API的文档中清楚地说明这一点。那么任何无效的使用都会导致UB,但这不是你的错。然而,处理原始指针是C风格的,C++程序员不喜欢。

    2. 采用(对的引用)封装指针类型,它总是经过合理的初始化,例如 std::string (而不是 const char* ), std::unique_ptr 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;
      }
      

      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;
      }
      

      这避免了原始指针,是C++处理此类情况的方法。后一种代码的一个问题是,它只适用于 unique_ptr 论据。人们可能会将其概括为重载(使用SFINAE或其他方式)以获取(const引用)任何类似自动指针的对象(例如定义为对象 obj 与成员一起 obj::get() const 返回一个 const obj::element_type* ).