2016-05-31 31 views
-1

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

Conditional jump depends on an uninitialized variable.

事实上,这是可能的未初始化的指针传递给函数功能

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

我应该忽略这样的错误吗?如果它们毫无用处,Valgrind为什么要麻烦告诉我他们呢?必须有我能做的事情。

+8

你确定它是初始化的指针吗? valgrind消息的结果就像'result'未初始化一样,如果'function'退出而没有返回,就会发生这种情况。 – delnan

+2

您的返回枚举的函数仍然可以有一个分支,其中枚举未初始化。我们无法看到导致valgrind错误的代码。 – stefaanv

+0

我相信*函数*不会退出而不返回。我不能发布完整的代码,但有一个* if-else *语句。 – user2738748

回答

1

你愿意走多远?如果有人想要破坏你的代码,他们会,你无法帮助它。

您应用的更有效的保护越难以得到。

一个简单的是检查NULL。这并不妨碍愚蠢的指针,但它可以防止有意识地失效。大多数人都对此感到满意。

然后你可以给这个指针一个包装类。实例化这个类需要指向一个有效的对象(或者通过一些无望的跳跃来使它成为一个无效的对象,这等于有意识地拍摄你的脚),所以不会出现未初始化指针的情景 - 但是对象可以在其之前不再存在指针被使用。

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

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

随着每一步,您的开销攀升到效果真正停止值得努力的水平。所以,只需检查该指针是否为NULL,并停止担心其他人为您提供帮助。

0

基本上有两种解决方案。

  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*)。

+0

'std :: unique_ptr'在这里没有什么可做的,因为参数的生命周期和管理都不是这个函数的业务。 – Quentin

+0

@Quentin对一个'unique_ptr'采取一个const引用不允许任何干扰指向的对象(包括它的生命期和管理),但只允许const访问。 – Walter

+0

的确,但你迫使调用者无缘无故地使用'std :: unique_ptr'。如果他使用'std :: shared_ptr',一个'boost :: scoped_ptr'或者只是一个具有自动生命期的对象呢? – Quentin

1

对于什么是“最佳”方法,意见会有所不同,因为根本不可能阻止某人通过坏的指针(例如未初始化的,悬挂的)指针。

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

一种方法是接受参考。编写代码以便它根本不使用原始指针,这使得使用错误的参数调用函数变得更加困难。限制是调用者仍然可以创建一个错误的引用(例如,通过解引用一个错误的指针),但是它需要更多的努力(或者如果不知情完成,需要更长的错误序列)来传递对函数的错误引用,而不是通过坏指针。

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

问题是,上述情况会导致无意中将不良数据传递给您的函数。没有什么技术可以绝对防止一个足够坚决的人(无论是通过天才还是愚蠢)找到一种方法来绕过所有的安全措施,并将错误的数据传递给你的功能。