Получение «free (): неверный указатель» с пользовательским интерпретатором TCL - PullRequest
0 голосов
/ 28 декабря 2010

У меня есть собственный интерпретатор 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?Что может вызвать этот сбой?

Ответы [ 2 ]

2 голосов
/ 29 декабря 2010

Возможно, вам лучше написать код, подобный следующему:

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;
}
0 голосов
/ 28 декабря 2010

Я считаю, что Tcl_Main должен быть таким:

Tcl_Main(argc, argv, Tcl_AppInit);

без адреса & оператора. Смотрите здесь здесь для примера. Насколько я понимаю, вы добавляете в Tcl_Main размер argv, чтобы обойти автоматическую обработку аргументов?

Редактировать: Что бы это ни стоило, я не вижу ничего явно неправильного в коде, но затем, учитывая мое умственное отклонение от адреса оператора, моё мнение может не стоить много. Можете ли вы без проблем поставить сценарии в стандартный интерпретатор tclsh? Они загружают какие-либо другие расширения или библиотеки?

...