从解决Cocos2dx-2.x arm64 Crash 来看C的奇技淫巧
最近把以前做的老游戏拿出来,重新编到手机上来玩玩,然后就有了以下的经历。
那时的引擎还是Cocos2dx-2.x,iPhone5还是高档机型。现在的机器是这样的,iPhone6S Plus我自用,今年iPhone7S都要出来了,真是时光荏苒,岁月如梭。
拿出我的CooolPad大神F2,OK。iPhone6S Plus,OK。Ad-Hoc,安装OK,运行,崩了。最令开发人员抓狂的事情出现了,Debug版OK,Release版崩溃。
这里还是要感谢一下Xcode,Release版也是可以调试的这个事情。最近逛论坛,有位童鞋把Debug版没问题Release版崩溃的应用不断的提到AppStore,被退回来的几乎崩溃,然后有位跟帖的好心人告诉他,把Scheme里的Run改成Release就可以调试了。
崩溃点在这里
~CURLRaii() { if (m_curl) curl_easy_cleanup(m_curl);//崩溃在这里 /* free the linked list for header data */ if (m_headers) curl_slist_free_all(m_headers); }
的确,不用curl_easy_cleanup(m_curl)就不崩溃了,但显然这不是个好办法。
然后就是各种搜索,有个人给出了下面的方法
bool perform(int *responseCode) { if (CURLE_OK != curl_easy_perform(m_curl)) return false; long temp = 0;//注意这里,用long类型去获得curl_easy_getinfo的信息 CURLcode code = curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &temp); *responseCode = (int)temp; if (code != CURLE_OK || *responseCode != 200) return false; // Get some mor data. return true; }
问题解决,显然这是C语言的老问题了,传指针的时候一定要类型匹配。以前没问题是因为32位平台上long和int是一样的,包括我现在的酷派大神。其实如果是原来的工程也应该是没问题的,因为那时的Cocos2dx中的curl是32位的静态库,但是现在AppStore要求必须要提供64位的版本,所以这个问题是无法掩盖了。现在的问题就是,这个崩溃的锅,应该由谁来背?
表面来看,这个锅是Cocos2dx的,君不见curl的接口描述是这样的,谁让你传int进去的?
/* * NAME curl_easy_getinfo() * * DESCRIPTION * * Request internal information from the curl session with this function. The * third argument MUST be a pointer to a long, a pointer to a char * or a * pointer to a double (as the documentation describes elsewhere). The data * pointed to will be filled in accordingly and can be relied upon only if the * function returns CURLE_OK. This function is intended to get used *AFTER* a * performed transfer, all results from this function are undefined until the * transfer is completed. */ CURL_EXTERN CURLcode curl_easy_getinfo(CURL * curl , CURLINFO info, ... ) ;
但请不要忘记,这是C语言,强类型的语言,出现这种因为类型不匹配引起的崩溃,责任可不全在调用者身上,我连个警告都没看到,谁知道你是怎么运作的?下面我们来看一下这个没有警告的“泛型”是怎么运作的。
首先,curl强调了这个函数有并且只有3个参数
/* This preprocessor magic that replaces a call with the exact same call is only done to make sure application authors pass exactly three arguments to these functions. */ #define curl_easy_setopt(handle, opt, param) curl_easy_setopt(handle,opt,param) #define curl_easy_getinfo(handle, info, arg) curl_easy_getinfo(handle,info,arg) #define curl_share_setopt(share, opt, param) curl_share_setopt(share,opt,param) #define curl_multi_setopt(handle, opt, param) curl_multi_setopt(handle,opt,param)
然后利用变长参数不检查类型的特点完成了这个戏法,本质上这和scanf是一样的,当初栽在这个函数上的童鞋们这回应该明白崩溃的原因了吧。
我很少用C语言的原因就是C的基础设施很少,C的奇技淫巧很多。虽然C++的坑更多,但是在不踩雷的情况下,还是能基本完成任务的。在C的领域内,你要是不会点此类的技巧,你几乎什么也做不了。
这也说明了,片面的强调编程语言应该是强类型还是弱类型都是不对的,C语言千方百计的要泛型,现在的脚本语言却逐渐开始强调类型声明。
最后,这个问题在C++中是可以避免的,也不用着上面的“技巧”。当然,我声明成long*或者long&,你非得传int就不能怨我了。