Smalltalk 在Visual Works图像中创建常量字符**

Smalltalk 在Visual Works图像中创建常量字符**,smalltalk,visualworks,gemstone,Smalltalk,Visualworks,Gemstone,我应该如何创建(const char**)以将其传递给C函数 假设我的常量字符**被命名为提示符,那么: user := 'User:' copyToHeap: #malloc:. pwd := 'Password:' copyToHeap: #malloc:. prompts := (ByteArray new: 64) copyToHeap: #malloc:. prompts copyAt: 0 from: (user referentAddress asByteArraySize

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

假设我的常量字符**被命名为
提示符
,那么:

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.
因此,
提示符
是一个64位的数组,其中前32位是指向
用户
的指针,第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.

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

prompts := CIntegerType char pointerType gcMalloc: 2.
然后可以按如下方式填充此数组:

prompts at: 0 put: user.
prompts at: 1 put: pwd.
注意,索引模仿C,如
提示[0]=user;提示[1]=pwd

最后一件事,所有你
malloc
,你必须
free
,否则你会得到内存泄漏

这意味着您应该更好地使用一些

["your protected code here"]
    ensure: [prompts free. user free. pwd free]`
……或者更糟

["your protected code here"]
    ensure:
        [prompts isNil ifFalse: [prompts free].
        "etc..."]`.
在早期开发中,我建议您最好使用
gcMalloc
gcMalloc:

思考之后

gcMalloc
对于
user
pwd
来说可能不是一个好主意

这是因为
提示
将获得
用户
pwd
对象中包含的内存地址副本:它将指向同一内存区域,但不会指向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堆关联的指针将被释放

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


由于DLLCC与C非常接近,所以在编写C代码时必须采取同样的谨慎。。。对于绝大多数C程序员来说,双指针是bug的来源。

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

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

  • 字节数组是字节(8位)。ByteArray新:64是64×8位。。。这不是你需要的东西……是的,它应该是全新的:8。以及一些位操作来设置字节数组上的地址。我的VW是32位为了使用#gcCalloc安全起见,您应该只在本地使用它来将数据传递给C函数。尽量避免从方法返回已分配的数据。您将不得不开始担心如何正确释放数据,而这通常比最初想象的要困难。要将字符串复制到堆中,请使用
    gccopyUnicode DestringToHeap
    或类似的方法,在其中可以指定编码。@Karsten yes在方法中隔离此类分配不是一个好主意。这只是为了说明gcMalloc的局限性。如果对象和C堆指针都无法保存到方法激活,那么使用gcMalloc和双指针仍然可以。但最好知道原因。至于编码,我通常在复制到堆之前发送#asbytearrayencode:。这样我们就可以传递C所期望的:字节。。。如果C使用ASCII,并且我们有如本例中所示的哑常量字符串,则不必这样做。是的,您是对的。但我们只有少数情况下可以使用VW C结构。也许在运行完所有程序后,我会切换到VW C结构。目前只有2种情况,其他情况可以使用基本指针轻松管理。