pascal字符串如何在内存中表示?

时间:2014-01-05 05:58:31

标签: c string freepascal

如何将pascal字符串放在内存中?

我读到:http://www.freepascal.org/docs-html/ref/refsu12.html 它表示字符串存储在堆上并且引用计数。为了确定存储长度和引用的位置,我创建了一个字符串并对其进行了大量测试:

type PInt = ^Integer;

var
    str: String;
begin
    str := 'hello';
    writeln(PInt(@str[1]) - (sizeof(integer) * 1)); //length
    writeln(PInt(@str[1]) - (sizeof(integer) * 2)); //reference count
end.

第一个打印长度,第二个打印参考计数。它做得非常好,而且很有效。

现在我试图在C中模仿同样的事情:

Export char* NewCString()
{
    const char* hello_ptr = "hello";

    int length = strlen(hello_ptr);

    //allocate space on the heap for:  sizeof(refcount) + sizeof(int) + strlength
    char* pascal_string = (char*)malloc((sizeof(int) * 2) + length);

    *((int*)&pascal_string[0]) = 0; //reference count to 0.
    *((int*)&pascal_string[sizeof(int)]) = length;  //length of the string.

    strcpy(&pascal_string[sizeof(int) * 2], hello_ptr); //copy hello to the pascal string.

    return &pascal_string[sizeof(int) * 2]; //return a pointer to the data.
}

Export void FreeCString(char* &ptr)
{
    int data_offset = sizeof(int) * 2;
    free(ptr - data_offset);
    ptr = NULL;
}

然后在pascal我做:

var
    str: string;
begin
    str := string(NewCString());
    writeln(PInt(@str[1]) - (sizeof(integer) * 1)); //length - prints 5. correct.
    writeln(PInt(@str[1]) - (sizeof(integer) * 2)); //reference count - prints 1! correct.
   //FreeCString(str);  //works fine if I call this..
end.

pascal代码正确打印长度,并且由于赋值,引用计数增加1。这是正确的。

但是,一旦执行完毕,它就会严重崩溃!它似乎试图释放字符串/堆。如果我自己调用FreeCString,它就可以了!我不确定发生了什么。

任何想法为什么会崩溃?

2 个答案:

答案 0 :(得分:1)

  1. “string”是一个别名,可以指向3种不同的字符串类型(shortstring,ansistring和unicodestring)
  2. ansistring和unicodestring改变了从FPC 2.6到FPC 2.7.x +的布局(等于Delphi 2007到Delphi 2009)
  3. 任何Delphi内存分配器都必须能够分辨出已分配块的大小。通常这是通过在块中放入32位大小来完成的。
  4. FreePascal和Delphi都有可插拔的内存分配器。默认的Free Pascal管理器是一个自己的子分配器。要使用(在* nix上)libc使用的任何内容,请使用unit cmem作为主程序中的第一个单元。
  5. 由于ansistring和unicodestring是使用手动技巧进行重新计算的,因此您负责维护引用计数的完整性。其中包括为Pascal< - >维护Pascal ABI。 C转换。
  6. 简而言之不要,以及必须的稀有情况,向pascal添加构造函数和析构函数,并通过它进行所有分配。

    P.S。你可能想看看rtl / inc / astrings.inc 在Windows上的P.s.2,最容易使用COM兼容的宽带(BSTR)来处理中介语字符串类型。

答案 1 :(得分:0)

仅仅因为运行时系统在内存中以特定方式布置字符串,并不意味着编写C代码来复制该内存布局将起作用。字符串管理可能涉及其他约束或外部数据结构。要使字符串与FreePascal兼容,请使用FreePascal自己的库例程。

听起来FreePascal除了free()之外还需要一些事情,当refcount变为零时,但如果没有一些逆向工程或深入研究ABI规范,它可能无法分辨出来。