Tcl转换double类型大型场景崩溃及Tcl_DictObjGet使用正确性咨询
核心问题答复
你当前的引用计数逻辑存在隐患,且缺少必要的错误校验,这是大场景下崩溃的核心原因;Tcl_DictObjGet的基础调用方式没问题,但没有处理返回值和边界情况,属于不完整的正确用法。
具体问题说明
- 直接崩溃原因
你没有对Tcl_DictObjGet的返回结果做校验,小场景下所有键都存在、传入的字典始终合法,所以运行正常。但大场景下一旦出现如下任意情况,代码都会直接崩溃:
- 目标key在字典中不存在,
Tcl_DictObjGet会将val_ptr设为NULL,你后续直接对空指针执行Tcl_IncrRefCount、传参给Tcl_GetDoubleFromObj都会触发内存访问错误 - 传入的
dict参数不是合法的Tcl字典对象,Tcl_DictObjGet执行失败,同样会返回空的val_ptr - 字典中对应key的值无法转换为double类型,
Tcl_GetDoubleFromObj执行异常,你没有做错误捕获
引用计数逻辑的缺陷
你给val_ptr加引用计数的时机不对:必须在确认Tcl_DictObjGet执行成功、且val_ptr非空之后再执行Tcl_IncrRefCount,否则对空指针操作引用计数本身就是非法操作。
补充说明:Tcl_DictObjGet返回的val_ptr是字典的内部引用,如果你能保证在data_handler执行期间字典对象不会被释放,也可以选择不对val_ptr额外增减引用计数,只要做好非空判断即可。额外隐藏问题
你写的Tcl过程定义语法有问题,proc my_ns::bla {后面缺少参数列表的闭合大括号,正确写法是proc my_ns::bla {} {,语法错误会导致脚本执行结果不符合预期,返回的结果不是你需要的列表,也会引发后续C接口的崩溃。
修正后代码示例
// 增加返回值方便上层判断执行结果 int Get_Dict_Value(Tcl_Interp *interp, Tcl_Obj *dict, const char* key, std::function<void(Tcl_Obj*)> data_handler) { // 先校验传入的dict是否为合法字典对象 if (Tcl_DictObjSize(interp, dict, NULL) != TCL_OK) { return TCL_ERROR; } Tcl_Obj* key_ptr = Tcl_NewStringObj(key, -1); Tcl_IncrRefCount(key_ptr); Tcl_Obj* val_ptr = NULL; int ret = Tcl_DictObjGet(interp, dict, key_ptr, &val_ptr); // 只有查找成功、值非空才执行后续操作 if (ret == TCL_OK && val_ptr != NULL) { Tcl_IncrRefCount(val_ptr); data_handler(val_ptr); Tcl_DecrRefCount(val_ptr); } Tcl_DecrRefCount(key_ptr); return ret; } void Write_Float_Dict(Tcl_Interp *interp, Tcl_Obj *dict, const char* key) { Get_Dict_Value(interp, dict, key, [&interp](Tcl_Obj *val_ptr) { double double_value; // 校验double转换是否成功 if (Tcl_GetDoubleFromObj(interp, val_ptr, &double_value) == TCL_OK) { std::cout << "the value: " << double_value << std::endl; } }); } static int Dict_Test(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { // 修正proc语法错误 const char* script = R"__( namespace eval my_ns {} proc my_ns::bla {} { set my_list "" set my_dict [dict create] dict set my_dict my_key 123789 dict set my_dict my_key_2 456 lappend my_list $my_dict return $my_list; } )__"; // 校验脚本执行结果 if (Tcl_Eval(interp, script) != TCL_OK) { return TCL_ERROR; } if (Tcl_Eval(interp, "my_ns::bla") != TCL_OK) { return TCL_ERROR; } Tcl_Obj* my_list = Tcl_GetObjResult(interp); Tcl_IncrRefCount(my_list); Tcl_Obj* my_dict; if (Tcl_ListObjIndex(interp, my_list, 0, &my_dict) == TCL_OK) { Tcl_IncrRefCount(my_dict); Write_Float_Dict(interp, my_dict, "my_key"); Write_Float_Dict(interp, my_dict, "my_key_2"); Tcl_DecrRefCount(my_dict); } Tcl_DecrRefCount(my_list); return TCL_OK; }
内容的提问来源于stack exchange,提问作者João Paulo
相关产品推荐
相关产品推荐

