一个测试引发的思考

82 篇文章 0 订阅
49 篇文章 0 订阅

有赖机缘,写代码展示null指针调用类的成员函数、静态成员函数: (想在一份代码中完成)

考虑到会有SIGSGEV(无效内存引用),所以要有异常\错误处理方法。



1. 首先想到C++的try & catch:

#include <stdlib.h>
#include <utility>
#include <iostream>
using namespace std;

class A
{
public:
    A() : m_i(0)
    {

    }

public:
    void test_not_access()
    {
        cout << "ok!" << endl;
    }

    void test_read()
    {
        int dummy = m_i;
        cout << "ok!" << endl;
    }

    void test_write()
    {
        ++m_i;
        cout << "ok!" << endl;
    }

    static void test_read_write_static()
    {
        int dummy = ++s_i;
        cout << "ok!" << endl;
    }

private:
    int m_i;
    static int s_i;
};

int A::s_i = 0;

int main(int argc, char * argv[], char * envp[])
{
    A * pa = NULL;

    typedef void (A::*PFunc)(void);
    typedef pair<PFunc, const char *> Pair;
    Pair msg[3] =
    {
        make_pair
        (
            &A::test_not_access,
            "null pointer call function which donot access member:\n\t"
        ),
        make_pair
        (
            &A::test_read,
            "null pointer call function which read member:\n\t"
        ),
        make_pair
        (
            &A::test_write,
            "null pointer call function which write member:\n\t"
        )
    };

    for (int i = 0, size = sizeof(msg)/sizeof(msg[0]); i < size; ++i)
    {
        try
        {
            cout << msg[i].second;
            (pa->*msg[i].first)();
        }
        catch (...)
        {
            cout << "error!" << endl;
        }
    }

    try
    {
        cout << "null pointer call static function which read and write member:\n\t";
        pa->test_read_write_static();
        exit(0);
    }
    catch (...)
    {
        cout << "error!" << endl;
    }

    return(0);
}

发现:try & catch (...) 不能处理SIGSGEV (不知道C++的set_handle?... 之后,能不能处理过来)



2. 然后想到信号处理机制 signal、sigaction:

#include <stdlib.h>
#include <signal.h>
#include <utility>
#include <iostream>
using namespace std;

void sig_segv(int signo)
{
    cout << "error!" << endl;
}

class A
{
public:
    A() : m_i(0)
    {

    }

public:
    void test_not_access()
    {
        cout << "ok!" << endl;
    }

    void test_read()
    {
        int dummy = m_i;
        cout << "ok!" << endl;
    }

    void test_write()
    {
        ++m_i;
        cout << "ok!" << endl;
    }

    static void test_read_write_static()
    {
        int dummy = ++s_i;
        cout << "ok!" << endl;
    }

private:
    int m_i;
    static int s_i;
};

int A::s_i = 0;

int main(int argc, char * argv[], char * envp[])
{
    if (SIG_ERR == signal(SIGSEGV, sig_segv))
    {
        cout << "signal failed" << endl;
        exit(1);
    }

    A * pa = NULL;

    typedef void (A::*PFunc)(void);
    typedef pair<PFunc, const char *> Pair;
    Pair msg[3] =
    {
        make_pair
        (
            &A::test_not_access,
            "null pointer call function which donot access member:\n\t"
        ),
        make_pair
        (
            &A::test_read,
            "null pointer call function which read member:\n\t"
        ),
        make_pair
        (
            &A::test_write,
            "null pointer call function which write member:\n\t"
        )
    };

    for (int i = 0, size = sizeof(msg)/sizeof(msg[0]); i < size; ++i)
    {
        cout << msg[i].second;
        (pa->*msg[i].first)();
    }

    cout << "null pointer call static function which read and write member:\n\t";
    pa->test_read_write_static();

    return(0);
}

发现:不停地打印error!,尝试用Stevens实现的signal和signal_intr代替系统自带的signal,效果一样!调试跟踪:原来,sig_sgev处理后,程序又会回去执行刚引发SIGSGEV信号的代码... 引发死循环!



3. 利用sigsetjmp、siglongjmp处理上面的问题:

#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <utility>
#include <iostream>
using namespace std;

sigjmp_buf jmpbuf;

void sig_segv(int signo)
{
    cout << "error!" << endl;
    siglongjmp(jmpbuf, 1);
}

class A
{
public:
    A() : m_i(0)
    {

    }

public:
    void test_not_access()
    {
        cout << "ok!" << endl;
    }

    void test_read()
    {
        int dummy = m_i;
        cout << "ok!" << endl;
    }

    void test_write()
    {
        ++m_i;
        cout << "ok!" << endl;
    }

    static void test_read_write_static()
    {
        int dummy = ++s_i;
        cout << "ok!" << endl;
    }

private:
    int m_i;
    static int s_i;
};

int A::s_i = 0;

int main(int argc, char * argv[], char * envp[])
{
    if (SIG_ERR == signal(SIGSEGV, sig_segv))
    {
        cout << "signal failed" << endl;
        exit(1);
    }

    A * pa = NULL;

    typedef void (A::*PFunc)(void);
    typedef pair<PFunc, const char *> Pair;
    Pair msg[3] =
    {
        make_pair
        (
            &A::test_not_access,
            "null pointer call function which donot access member:\n\t"
        ),
        make_pair
        (
            &A::test_read,
            "null pointer call function which read member:\n\t"
        ),
        make_pair
        (
            &A::test_write,
            "null pointer call function which write member:\n\t"
        )
    };

    volatile int i = 0;
    const int size = sizeof(msg)/sizeof(msg[0]);

    if (0 != sigsetjmp(jmpbuf, 1))
    {
        ++i;
    }

    for ( ; i < size; ++i)
    {
        cout << msg[i].second;
        (pa->*msg[i].first)();
    }

    cout << "null pointer call static function which read and write member:\n\t";
    pa->test_read_write_static();

    return(0);
}

现在能全部打印:

null pointer call function which donot access member:
    ok!
null pointer call function which read member:
    error!
null pointer call function which write member:
    error!
null pointer call static function which read and write member:
    ok!


4. 用多进程 fork 来做:

#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <utility>
#include <iostream>
using namespace std;

void sig_segv(int signo)
{
    cout << "error!" << endl;
    exit(0);
}

class A
{
public:
    A() : m_i(0)
    {

    }

public:
    void test_not_access()
    {
        cout << "ok!" << endl;
    }

    void test_read()
    {
        int dummy = m_i;
        cout << "ok!" << endl;
    }

    void test_write()
    {
        ++m_i;
        cout << "ok!" << endl;
    }

    static void test_read_write_static()
    {
        int dummy = ++s_i;
        cout << "ok!" << endl;
    }

private:
    int m_i;
    static int s_i;
};

int A::s_i = 0;

int main(int argc, char * argv[], char * envp[])
{
    if (SIG_ERR == signal(SIGSEGV, sig_segv))
    {
        cout << "signal failed" << endl;
        exit(1);
    }

    A * pa = NULL;

    pid_t pid = 0;

    typedef void (A::*PFunc)(void);
    typedef pair<PFunc, const char *> Pair;
    Pair msg[3] =
    {
        make_pair
        (
            &A::test_not_access,
            "null pointer call function which donot access members:\n\t"
        ),
        make_pair
        (
            &A::test_read,
            "null pointer call function which read members:\n\t"
        ),
        make_pair
        (
            &A::test_write,
            "null pointer call function which write members:\n\t"
        )
    };

    for (int i = 0, size = sizeof(msg)/sizeof(msg[0]); i < size; ++i)
    {
        if ((pid = fork()) < 0)
        {
            cout << "fork failed" << endl;
            exit(1);
        }
        else if (pid == 0)
        {
            cout << msg[i].second;
            (pa->*msg[i].first)();
            exit(0);
        }
        else
        {
            sleep(1);
        }
    }

    if ((pid = fork()) < 0)
    {
        cout << "fork failed" << endl;
        exit(1);
    }
    else if (pid == 0)
    {
        cout << "null pointer call static function which read and write member:\n\t";
        pa->test_read_write_static();
        exit(0);
    }
    else
    {
        sleep(1);
    }

    return(0);
}

也可以全部打印;当然,我们还可以用TELL_WAIT那组函数替代这里的sleep,来加快效率。


 

其实,3是在4之后想到的,因为感觉4不是太好,所以才从2中想到了3。1怎么解决呢? 是个问题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值