空字符串在参数传递过程中莫名变成空指针,导致程序崩溃,寻高人指教。

kantaguo 2008-04-15 10:29:36
原来在unix平台下的代码现在要移植到linux上(redHat AS3, gcc 3.2.3),出现问题,在做函数调用的时候一个空字符串传递为参数的时候变成了空指针,导致程序崩溃,百思不得其解,请高人指教。我本身没有积分可以送,还望大家不吝赐教,谢谢。

下面是示例代码:


#include <iostream>
#include <string>

using namespace std;

struct TimeStamp
{
long year; ///< A year of a time stamp.
long month; ///< A month of a time stamp.
long day; ///< A day of a time stamp.
long hour; ///< A hour of a time stamp.
long minute; ///< A minute of a time stamp.
long second; ///< A second of a time stamp.
};

class TypeConv
{
public:
static TimeStamp toTimeStamp(const char* aString);
static TimeStamp toTimeStamp(const std::string& aString);
};

inline
TimeStamp
TypeConv::
toTimeStamp(const std::string& aString)
{
std::string str = ( aString.c_str() == 0 ) ? std::string() : aString;
if ( str.size() <= 14 )
str.append(" ", 14 - str.size());
else
str.append(" ", 14);

std::string strYear (str, 0, 4);
std::string strMonth (str, 4, 2);
std::string strDay (str, 6, 2);
std::string strHour (str, 8, 2);
std::string strMinute(str, 10, 2);
std::string strSecond(str, 12, 2);

TimeStamp timeStamp;
timeStamp.year = ::atol(strYear.c_str());
timeStamp.month = ::atol(strMonth.c_str());
timeStamp.day = ::atol(strDay.c_str());
timeStamp.hour = ::atol(strHour.c_str());
timeStamp.minute = ::atol(strMinute.c_str());
timeStamp.second = ::atol(strSecond.c_str());

return timeStamp;
}

inline
TimeStamp
TypeConv::
toTimeStamp(const char* aString)
{
return ( ::strcmp(aString, "") == 0 ) // 在此处会崩溃,用gdb调试时显示aString=0x0,为什么会这样,如何避免?
? TypeConv::toTimeStamp(std::string())
: TypeConv::toTimeStamp(std::string(aString));
}

int main()
{
cout << "begin" << endl;

string sTimeStr = "";

//const char* str = NULL;

TimeStamp time = TypeConv::toTimeStamp(sTimeStr.c_str());

cout << "end" << endl;

return 0;
}
...全文
550 19 打赏 收藏 举报
写回复
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
meiZiNick 2008-05-01
  • 打赏
  • 举报
回复
不知,帮顶
kantaguo 2008-04-28
  • 打赏
  • 举报
回复
再自己顶起来一次,如果没办法只能做无满意结帖了。
kantaguo 2008-04-22
  • 打赏
  • 举报
回复
怎么都没人进来了,顶起来继续寻求帮助。
two_ears 2008-04-16
  • 打赏
  • 举报
回复
上面给你贴的用color=#FF0000标颜色的地方出问题了,呵呵,凑合看吧,加了一点注释
是我用vc的debug,step in看了一下,linux的人好像都不屑于vc,呵呵
VC中的实现可以看到,肯定不会分配内存,grow也返回了false
mingw的gdb显示string内部指针为"",可见两种实现有区别
kantaguo 2008-04-16
  • 打赏
  • 举报
回复
补充一句,我的程序是个demon的多线程server程序。
two_ears 2008-04-16
  • 打赏
  • 举报
回复
深了,学习
d
kantaguo 2008-04-16
  • 打赏
  • 举报
回复
首先感谢楼上各位的热心帮助,今天在公司用gdb仔细调试了一下发现问题好像并非是或者并不全是我在主帖中所描述,由此可能会给大家带来的误导深表歉意。

今天在公司对我的代码做了一些修改,但是仍然没有实际解决问题,只是能做出一些排除,因此还是需要和大家讨论,请各位帮忙分析。

以下是我对代码做的修改及修改前后gdb调试时的堆栈信息:

修改后代码

inline
TimeStamp
TypeConv::
toTimeStamp(const char* aString)
{
if( aString == NULL ) // 判断了空指针的情况,但是发现实际是根本不会出现空指针,每次依然是以""的形式进入
{
return TypeConv::toTimeStamp(std::string());
}
else // 下面实际只是将原来的代码拆分开来写
{
if(::strcmp(aString, "") == 0)
{
//std::string tmpStr = "20080416";
//TimeStamp t = TypeConv::toTimeStamp(tmpStr);

TimeStamp t = TypeConv::toTimeStamp(std::string()); // 用gdb单步调试在这里会crash,堆栈信息如后附。
// 更为奇怪的是即使我使用上面注释掉的两行代码(使用常量)依然会出问题,
// 而且同样的参数传入的时候并不是每次都会crash,网上有资料称某些版本gcc
// 的basic_string构造函数存在sharded memory管理bug,可能会导致
// segfault,不确定是否是此原因。
return t;
}
else
{
TimeStamp t = TypeConv::toTimeStamp(std::string(aString));
return t;
}

/*
return ( ::strcmp(aString, "") == 0 )
? TypeConv::toTimeStamp(std::string())
: TypeConv::toTimeStamp(std::string(aString));
*/
}
}


代码修改前的堆栈:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 193354672 (LWP 14494)]
0x0055eb16 in std::__default_alloc_template<true, 0>::allocate () from /usr/lib/libstdc++.so.5
(gdb) bt
#0 0x0055eb16 in std::__default_alloc_template<true, 0>::allocate () from /usr/lib/libstdc++.so.5
#1 0x00564b48 in std::string::_Rep::_S_create () from /usr/lib/libstdc++.so.5
#2 0x0807fd71 in std::string::_S_construct<__gnu_cxx::__normal_iterator<char*, std::string> > (__a=@0xb865280) at /usr/include/c++/3.2.3/bits/basic_string.tcc:154
#3 0x0807abb6 in IFWI::TypeConv::toTimeStamp (aString=@0x0) at /usr/include/c++/3.2.3/bits/basic_string.h:732
#4 0x057d78fe in IFWI::TIB::CommonExecuter::SET_EVENT_INFO (aMessageBody=@0xb8655f0, aEventInfo=@0xb865560, aReasonCodeRequired=false)
at /usr/include/c++/3.2.3/bits/stl_alloc.h:668
#5 0x058307e6 in IFWI::TIB::LotExecuter::makeLoggedOut (aMessageBody=@0xb8655f0, aReplyMessageBody=@0xb8657e0) at LotExecuter.cpp:726
#6 0x08066a96 in IFWI::TIB::WIPsrv::LOT_CREATE_S_REQ (this=0xbfffa810, aMessageName=0xb742050a "LOT_CREATE_S", aMessageBody=@0xb865800, aReplyMessageBody=@0xb8657e0)
at WIPsrv.cpp:1294
#7 0x08059b3f in IFWI::TIB::TService<IFWI::TIB::WIPsrv>::onRequest (this=0xbfffa810, aMessage=@0xb865930) at /data/mesapp/include/IFWTService.i:645
#8 0x003a9a1e in IFWI::TIB::RequestListener::onMsg (this=0x0, aMessage=@0xb865930) at IFWTService.cpp:328
#9 0x003a8803 in IFWI::TIB::MsgCallback::onMsg (this=0x0, aListener=0x9057bf8, aMessage=@0xb865930) at IFWTService.cpp:226
#10 0x05a1db4f in TibrvMsgCallback::onEvent () from /data/mesapp/lib/libNcTGEN.so.1.3.1
#11 0x05a1d346 in TibrvEvent::_listenCB () from /data/mesapp/lib/libNcTGEN.so.1.3.1
#12 0x0062dea0 in _tibrvQueue_Dispatch () from /usr/tibco/tibrv/lib/libtibrv.so
#13 0x0062e330 in tibrvQueueGroup_TimedDispatch () from /usr/tibco/tibrv/lib/libtibrv.so
#14 0x0062e5c3 in _tibrvDispatcher_Run () from /usr/tibco/tibrv/lib/libtibrv.so
#15 0x003dddd8 in start_thread () from /lib/tls/libpthread.so.0
#16 0x05f9cd1a in clone () from /lib/tls/libc.so.6

代码修改后的堆栈:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 193354672 (LWP 14494)]
0x0055eb16 in std::__default_alloc_template<true, 0>::allocate () from /usr/lib/libstdc++.so.5
(gdb) bt
#0 0x0055eb16 in std::__default_alloc_template<true, 0>::allocate () from /usr/lib/libstdc++.so.5
#1 0x00564b48 in std::string::_Rep::_S_create () from /usr/lib/libstdc++.so.5
#2 0x0807fd71 in std::string::_S_construct<__gnu_cxx::__normal_iterator<char*, std::string> > (__a=@0xb865280) at /usr/include/c++/3.2.3/bits/basic_string.tcc:154
#3 0x0807abb6 in IFWI::TypeConv::toTimeStamp (aString=@0x0) at /usr/include/c++/3.2.3/bits/basic_string.h:732
#4 0x057d78fe in IFWI::TIB::CommonExecuter::SET_EVENT_INFO (aMessageBody=@0xb8655f0, aEventInfo=@0xb865560, aReasonCodeRequired=false)
at /usr/include/c++/3.2.3/bits/stl_alloc.h:668
#5 0x058307e6 in IFWI::TIB::LotExecuter::makeLoggedOut (aMessageBody=@0xb8655f0, aReplyMessageBody=@0xb8657e0) at LotExecuter.cpp:726
#6 0x08066a96 in IFWI::TIB::WIPsrv::LOT_CREATE_S_REQ (this=0xbfffa810, aMessageName=0xb742050a "LOT_CREATE_S", aMessageBody=@0xb865800, aReplyMessageBody=@0xb8657e0)
at WIPsrv.cpp:1294
#7 0x08059b3f in IFWI::TIB::TService<IFWI::TIB::WIPsrv>::onRequest (this=0xbfffa810, aMessage=@0xb865930) at /data/mesapp/include/IFWTService.i:645
#8 0x003a9a1e in IFWI::TIB::RequestListener::onMsg (this=0x0, aMessage=@0xb865930) at IFWTService.cpp:328
#9 0x003a8803 in IFWI::TIB::MsgCallback::onMsg (this=0x0, aListener=0x9057bf8, aMessage=@0xb865930) at IFWTService.cpp:226
#10 0x05a1db4f in TibrvMsgCallback::onEvent () from /data/mesapp/lib/libNcTGEN.so.1.3.1
#11 0x05a1d346 in TibrvEvent::_listenCB () from /data/mesapp/lib/libNcTGEN.so.1.3.1
#12 0x0062dea0 in _tibrvQueue_Dispatch () from /usr/tibco/tibrv/lib/libtibrv.so
#13 0x0062e330 in tibrvQueueGroup_TimedDispatch () from /usr/tibco/tibrv/lib/libtibrv.so
#14 0x0062e5c3 in _tibrvDispatcher_Run () from /usr/tibco/tibrv/lib/libtibrv.so
#15 0x003dddd8 in start_thread () from /lib/tls/libpthread.so.0
#16 0x05f9cd1a in clone () from /lib/tls/libc.so.6

其实修改前和修改后几乎是相同的,导致crash的都是string的构造,但是为什么呢???

单步调试时死在gcc源文件的这一句 _Rep::_S_create(__dnew, __a) , 这就看不懂是什么了,
另外谁能告诉我 _GLIBCXX_FULLY_DYNAMIC_STRING 这个编译参数的作用是什么以及如何设置,在gcc的头文件中看到有关于他的使用
template<typename _CharT, typename _Traits, typename _Alloc>
inline basic_string<_CharT, _Traits, _Alloc>::
basic_string()
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
: _M_dataplus(_S_empty_rep()._M_refcopy(), _Alloc()) { }
#else
: _M_dataplus(_S_construct(size_type(), _CharT(), _Alloc()), _Alloc()) { }
#endif

以上望大家予以指点,在下感激不尽,因为在公司的时候上不了csdn,所以只能晚上回家来向大家讨教,见谅。
two_ears 2008-04-15
  • 打赏
  • 举报
回复
主要就是看构造函数怎么处理""了
two_ears 2008-04-15
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 kantaguo 的回复:]
引用 8 楼 two_ears 的回复:
可能就是stl的实现不同,我也用mingw试了,确实没问题,但是vc6就有问题,0长的字符串它不分配内存
这样应该就不会异常了:

C/C++ codeint main()
{
cout < < "begin" < < endl;

string sTimeStr = "ABC";

//const char* str = NULL;

TimeStamp time = TypeConv::toTimeStamp(sTimeStr.c_str());

cout < < "end" < < endl…
[/Quote]
“莫非真的就是因为stl实现不同导致这个原因?”嗯,我觉得可能
刚才我是用同样的代码,用cl和mingw分别编译,结果前者是NULL后者是""
开始以为mingw会得到你说的结果呢,因为不是说mingw移植自linux的gcc么
其实stl代码也可以看到,下面的是VC6环境下step in到string str="";看到的执行过程

basic_string(const _E *_S, const _A& _Al = _A())
: allocator(_Al) {_Tidy(), assign(_S); } // _Tidy使_Ptr=0
_Myt& assign(const _E *_S)
{return (assign(_S, _Tr::length(_S))); }
_Myt& assign(const _E *_S, size_type _N) // _N是0
{if (_Grow(_N, true)) // 返回false
{....}
return (*this); }
bool _Grow(size_type _N, bool _Trim = false)
{if (max_size() < _N)....
if (_N == 0)
{if (_Trim)
_Tidy(true); // 所以这次_Ptr还是0
else if (_Ptr != 0)
_Eos(0);
return (false); }
else....}
void _Tidy(bool _Built = false)
{if (!_Built || _Ptr == 0)
;
else if ...
else...
_Ptr = 0, _Len = 0, _Res = 0; }
kantaguo 2008-04-15
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 baishawojia 的回复:]

  常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化
[/Quote]

我没太懂你的意思,你是要我去初始化什么? sTimeStr ? 如何初始化呢? 我已经负值为 “”了啊。
kantaguo 2008-04-15
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 two_ears 的回复:]
可能就是stl的实现不同,我也用mingw试了,确实没问题,但是vc6就有问题,0长的字符串它不分配内存
这样应该就不会异常了:

C/C++ codeint main()
{
cout << "begin" << endl;

string sTimeStr = "ABC";

//const char* str = NULL;

TimeStamp time = TypeConv::toTimeStamp(sTimeStr.c_str());

cout << "end" << endl;

return 0;
}


另外可以在函数toT…
[/Quote]

如果不是空字符串当然是没问题的了,在toTimeStamp内先进行检查是可以的,今天下班前写了一点还没验证。
楼上几位都提到stl的实现问题,莫非真的就是因为stl实现不同导致这个原因?
two_ears 2008-04-15
  • 打赏
  • 举报
回复
可能就是stl的实现不同,我也用mingw试了,确实没问题,但是vc6就有问题,0长的字符串它不分配内存
这样应该就不会异常了:
int main()
{
cout << "begin" << endl;

string sTimeStr = "ABC";

//const char* str = NULL;

TimeStamp time = TypeConv::toTimeStamp(sTimeStr.c_str());

cout << "end" << endl;

return 0;
}

另外可以在函数toTimeStamp里加一个检查,应该改动不大吧?
baishawojia 2008-04-15
  • 打赏
  • 举报
回复

  常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化
kantaguo 2008-04-15
  • 打赏
  • 举报
回复
谢谢楼上两位的答复,我明天到公司试试看,另外到公司后我会把gdb调试时的堆栈信息贴出来请大家帮忙分析。

改函数的参数类型当然是一种办法,但是因为整个系统非常庞大,改参数类型要改的地方就太多了,而且也不打算因为平台的原因就采用不同的参数类型,因此首选还是解决空指针的问题,而且我确实也很好奇为什么会发生这个转换。
effective_person 2008-04-15
  • 打赏
  • 举报
回复
Unix平台我没有试过!不过window vs中确实可以这样写!
各种可能的写法都试试吧!
two_ears 2008-04-15
  • 打赏
  • 举报
回复
看看string sTimeStr = "";之后string有没有分配内存,可能和stl的实现有关系吧
我是风 2008-04-15
  • 打赏
  • 举报
回复
我在MinGW下编译执行,没有问题啊。
effective_person 2008-04-15
  • 打赏
  • 举报
回复

inline TimeStamp TypeConv::toTimeStamp(const char* aString)//aString是个指针 指向的空。
{
return ( ::strcmp(aString, "") == 0 ) ? TypeConv::toTimeStamp(std::string()): TypeConv::toTimeStamp(std::string(aString));
}


inline
TimeStamp
TypeConv::
toTimeStamp(const std::string& aString)// 该成string类型看看
{
return ( aString==0 ) ? TypeConv::toTimeStamp(std::string()): TypeConv::toTimeStamp(std::string(aString));
}


TimeStamp time = TypeConv::toTimeStamp(sTimeStr);//试试看看

ouyh12345 2008-04-15
  • 打赏
  • 举报
回复
这样传会出错吗?
const char* str = sTimeStr.c_str();
TimeStamp time = TypeConv::toTimeStamp(str);
相关推荐
发帖
C++ 语言

6.3w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
帖子事件
创建了帖子
2008-04-15 10:29
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下