【问题标题】:How are pascal strings represented in memory?帕斯卡字符串在内存中是如何表示的?
【发布时间】:2014-01-05 05:58:31
【问题描述】:

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;
}

然后在帕斯卡我做:

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,它工作得很好!我不确定发生了什么。

知道为什么会崩溃吗?

【问题讨论】:

  • 您混淆了 Pascal 的多个版本(Wirth/Turbo Pascal 在字节 0 中定义长度,Delphi 2 引入长字符串之后的所有内容都不会,除非它们被声明为 ShortString)。您在标签中列出了四种不同的语言。相反,你为什么不首先解释你真正想要完成的事情,并询问如何做到这一点?它为什么崩溃是因为你对不正确的事情做出了错误的假设。
  • 那里。我已经缩小了语言范围。我正在尝试将 c 样式的字符串转换为帕斯卡字符串,而不必同时将长度作为参数传递。
  • “我正在尝试将 c-stype 字符串转换为 pascal 字符串”是什么意思? Delphi/Free Pascal 可以很好地接受空终止的 C 风格字符串,而无需长度参数;它在每个单独的 Windows 应用程序中完成了数千次(通过 WinAPI 调用)。再说一遍,你到底想完成什么?
  • char* &ptr 是 C++。不确定是否要重新标记问题。
  • @KenWhite 我尝试正常操作,但出现访问冲突。我也尝试过使用 PChar。这只会打印我的字符串中的第一个字符。

标签: c string freepascal


【解决方案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 C 转换维护 Pascal ABI。

简而言之不要,在罕见的情况下,你必须向pascal添加一个构造函数和一个析构函数,并通过它进行所有分配。

附:你可能想看看 rtl/inc/astrings.inc P.s.2 在 Windows 上,将 COM 兼容的宽字符串 (BSTR) 用于跨语言字符串类型可能是最简单的。

【讨论】:

  • 我通过将引用计数更改为 -1 来修复它。让帕斯卡认为字符串是不变的。因此,它从不试图“释放”我的琴弦,而我自己来处理。现在工作正常。不过我会试试你说的。
【解决方案2】:

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

听起来 FreePascal 需要在引用计数为零时发生除 free() 之外的其他事情,但如果没有一些逆向工程或深入研究 ABI 规范,可能无法判断是什么。

【讨论】:

    猜你喜欢
    • 2014-09-23
    • 1970-01-01
    • 2014-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    相关资源
    最近更新 更多