有赖机缘,写代码展示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怎么解决呢? 是个问题!