【问题标题】:Getting "free(): invalid pointer" with a custom TCL interpreter使用自定义 TCL 解释器获取“free(): invalid pointer”
【发布时间】:2011-05-31 16:55:37
【问题描述】:

我有一个自定义的 TCL 解释器。这里是:

// file main.cpp
#include <tcl.h>
#include <string>

int    argc = 0;
char** argv = 0;

int
Tcl_AppInit( Tcl_Interp* interp ) 
{
    if ( Tcl_Init( interp ) == TCL_ERROR ) {
        return TCL_ERROR;  
    }
    for ( int i = 1; i < argc; ++i ) {             
        if ( Tcl_Eval( interp, ("source " + std::string( argv[i] )).c_str() ) == TCL_ERROR ) {
            return TCL_ERROR;
        }
    }
    return TCL_OK;
}

int
main( int argc, char** argv )
{
    ::argc = argc;
    ::argv = argv;
    Tcl_Main( 1, argv, &Tcl_AppInit );
    return 0;
}

我使用以下命令构建main.cpp

g++ -DNDEBUG -O3 -fpic -Wall -pedantic -fno-strict-aliasing \
    -Wl,-R/usr/local/lib -L/usr/local/lib -ltcl main.cpp -o myinterp

有时myinterp 会因为这样的错误消息而崩溃:

free(): invalid pointer: 0x00002b04078aa000 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3723c722ef]
/lib64/libc.so.6(cfree+0x4b)[0x3723c7273b]
/lib64/libc.so.6(_IO_free_backup_area+0x18)[0x3723c6e1d8]
/lib64/libc.so.6(_IO_file_overflow+0x1d2)[0x3723c6c1d2]
/lib64/libc.so.6(_IO_file_xsputn+0xf2)[0x3723c6ce22]
/lib64/libc.so.6(_IO_vfprintf+0x1b0)[0x3723c428a0]
/lib64/libc.so.6(_IO_fprintf+0x88)[0x3723c4d358]

main.cpp 有问题吗?什么可能导致此崩溃?

【问题讨论】:

    标签: c++ malloc free tcl glibc


    【解决方案1】:

    你最好这样写代码:

    int
    Tcl_AppInit( Tcl_Interp* interp )
    {
        if ( Tcl_Init( interp ) == TCL_ERROR ) {
            return TCL_ERROR;
        }
        for ( int i = 1; i < argc; ++i ) {
            std::string script("source ");
    
            script += argv[i];
            if ( Tcl_Eval( interp, script.c_str() ) == TCL_ERROR ) {
                return TCL_ERROR;
            }
        }
        return TCL_OK;
    }
    

    这样,std::string 中缓冲区的生命周期将是正确的,我的直觉表明这可能是您真正的问题。 (一旦有损坏的内存,崩溃几乎可以在任何地方突然出现。)但是,您还应该知道,如果您在任何这些文件名中有空格,这仍然会完全出错。也许这对你来说没问题(例如,如果它是所有具有“好”名称的本地文件名)但否则使用 Tcl 的 Tcl_EvalObjv 来执行,就像这样(有点长;它真的是 C,而不是 C++):

    int
    Tcl_AppInit( Tcl_Interp* interp ) 
    {
        Tcl_Obj *script[2];
        int code = TCL_OK;
    
        if ( Tcl_Init( interp ) == TCL_ERROR ) {
            return TCL_ERROR;
        }
        script[0] = Tcl_NewStringObj("source", -1);
        Tcl_IncrRefCount(script[0]);
        for ( int i = 1; i < argc && code == TCL_OK; ++i ) {
            script[1] = Tcl_NewStringObj(argv[i], -1);
            Tcl_IncrRefCount(script[1]);
            if (Tcl_EvalObjv(interp, 2, script, 0/*no special flags*/) != TCL_OK) {
                code = TCL_ERROR;
            }
            Tcl_DecrRefCount(script[1]);
        }
        Tcl_DecrRefCount(script[0]);
        return code;
    }
    

    【讨论】:

    • 考虑“好”文件名的情况。我认为拨打Tcl_Eval( interp, ("source " + std::string( argv[i] )).c_str() ) 没有问题。我称之为c_str() 的临时std::string 对象一直存在到创建它的块的末尾,对吗?所以它在每次迭代结束时在for 循环中被销毁,所以在Tcl_Eval 执行期间,该临时对象仍然存在。我错了吗?
    • @Vahagn:我会非常谨慎地使用这种假设。另外,您可以使用 Tcl_VarEval 让 Tcl 为您进行连接。
    【解决方案2】:

    我认为Tcl_Main应该是这样的:

    Tcl_Main(argc, argv, Tcl_AppInit);
    

    没有 & 运算符的地址。有关示例,请参见此处here。我认为您在向 Tcl_Main 谎报 argv 的大小以绕过参数的自动处理?

    编辑:对于它的价值,我看不出代码有任何明显的错误,但是考虑到我对操作员地址的精神失常,我的意见可能并不值得。您能否将脚本源代码放入标准的 tclsh 解释器中而没有任何问题?他们是否加载任何其他扩展或库?

    【讨论】:

    • Tcl_AppInit&amp;Tcl_AppInit 相同。
    • Doh - 是的,当然是这样,而且更正确,因为它表明您确实打算获取地址而不是调用函数。
    • @Jackson 是的,我在撒谎argv 的大小。编写此自定义 TCL 解释器的目的是在 myinterp some.tcl 处理 some.tcl 并且不存在交互式 TCL 会话时遇到情况。如果您能提出另一种更好的方法来实现这一目标,我将不胜感激。
    • @Vahagn:wiki.tcl.tk/1968 有一些代码(在 cmets 中;点击显示)在纯 Tcl 中实现命令循环(即 REPL)。只要有一个包含该代码的脚本,您就可以与 Tcl 解释器交互,尽管从文件加载代码。或者你可以使用 Tclx 包中更完善的东西。
    • @Donal Fellows 我使用的是 Tclx 包,但它会导致 Tk 出现一些问题。在commandloop -interactive on 之后,如果您不永久调用update 命令,所有GUI 都会挂起。
    猜你喜欢
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多