在 Visual Works 图像中创建一个 const char **

时间:2021-02-19 18:12:19

标签: smalltalk visualworks gemstone

我应该如何创建一个 (const char **) 以将其传递给 C 函数?

假设我的 const char ** 被命名为 prompts 然后:

user := 'User:' copyToHeap: #malloc:.    
pwd := 'Password:' copyToHeap: #malloc:.
prompts := (ByteArray new: 64) copyToHeap: #malloc:.
prompts copyAt: 0 from: (user referentAddress asByteArraySize: 32) size: 4 startingAt: 1.
prompts copyAt: 31 from: (pwd referentAddress asByteArraySize: 32) size: 4 startingAt: 1.

所以 prompts 是一个 64 位数组,其中前 32 位是指向 user 的指针,而后 32 位是指向 pwd 的指针。

但是 C 函数不起作用。 在 GemStone 中工作正常:

prompts := CByteArray gcMalloc: 16.
user := CByteArray withAll: 'User:'.
pwd := CByteArray withAll: 'Password:'.
prompts uint64At: 0 put: user memoryAddress.
prompts uint64At: 8 put: pwd memoryAddress.

2 个答案:

答案 0 :(得分:2)

DLLCC 提供了一些非常接近 C 的 API。 您需要一个包含两个字符指针的数组。

prompts := CIntegerType char pointerType gcMalloc: 2.

然后你可以像这样填充这个数组:

prompts at: 0 put: user.
prompts at: 1 put: pwd.

请注意,索引像 prompts[0]=user; prompts[1]=pwd; 一样模仿 C。

最后一件事,所有你malloc,你必须然后free,否则你会出现内存泄漏。

这意味着你应该更好地保护所有这些代码

["your protected code here"]
    ensure: [prompts free. user free. pwd free]`

...或更糟...

["your protected code here"]
    ensure:
        [prompts isNil ifFalse: [prompts free].
        "etc..."]`.

在早期开发中,我建议你最好使用gcMallocgcMalloc:

事后思考

gcMallocuserpwd来说可能不是一个好主意。

这是因为 prompts 将获得包含在 userpwd 对象中的内存地址的副本:它将指向相同的内存区域,但不会指向 Smalltalk对象...

gcMalloc 只监控 Smalltalk 对象的垃圾回收。因此,如果不再使用 Smalltalk 对象,尽管其他一些对象指向同一个 C 堆,但 C 堆可能会过早地被释放......

示例:

fillPrompts
    | user pwd prompts |
    user := 'User:' copyToHeap: #gcMalloc:.    
    pwd := 'Password:' copyToHeap: #gcMalloc:.
    prompts := CIntegerType char pointerType gcMalloc: 2.
    prompts at: 0 put: user.
    prompts at: 1 put: pwd.
    ^prompts

copyToHeap: 创建一个 CPointer 对象。只要该方法处于活动状态,它的上下文就会指向这些对象(通过堆栈上的插槽)。
但是这个方法返回后,没有任何对象指向CPointer对象。
如果发生了一些垃圾回收,它们关联的指向 C 堆的指针将被释放。

但是 prompts 仍然包含对已释放内存的引用(所谓的悬空指针)。

DLLCC 与 C 非常接近,必须像编写 C 代码一样小心...而双指针是绝大多数 C 程序员的错误来源。

答案 1 :(得分:2)

你不应该直接处理字节。这在 C 中甚至没有意义。

  1. 使用两个 char* 成员创建一个结构体,这样更容易声明、创建和处理。
  2. 使用 #gcCalloc#gcCopyToHeap 以便在仍自动释放的堆上分配内存。通常,使用这些方法是安全的,因为您只需要在单个方法中使用该内存将其传输到 C。假设是 c 函数会复制此内存以备后用。
  3. 您可以使用 #memberAt:put: 为结构分配成员。