我有一个 C 文件和一个 Raku 文件用于连接。有时我会得到随机且错误的值。文件如下
\nC 文件:它有一个名为 CField 的结构,Raku 代码将与该结构交互。重要的是它有一个动态分配的属性,即data
. 所以我在 中进行了 2 个 malloc make_field
,并在 中进行了 2 个 free free_field
。
// field.c\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\nstruct CField {\n void* data;\n size_t size;\n};\n\nvoid* checker_malloc(size_t n) {\n // malloc but checks if allocated fine\n void* p = malloc(n);\n if (!p) { fprintf(stderr, "can\'t allocate %zu bytes", n); exit(1); }\n return p;\n}\n\nstruct CField* make_field(void* data, size_t data_size) {\n // malloc for all struct\n struct CField* new_field = checker_malloc(sizeof *new_field);\n\n // malloc for .data\n size_t num_bytes = data_size * sizeof(int32_t);\n new_field->data = checker_malloc(num_bytes);\n memcpy(new_field->data, data, num_bytes);\n\n new_field->size = data_size;\n \n return new_field;\n}\n\nvoid free_field(struct CField* v) {\n free(v->data);\n free(v);\n}\n
Run Code Online (Sandbox Code Playgroud)\nRaku 文件:它有一个围绕该结构的包装器。我[]
在这里为“无论传递什么”定义运算符,Wrapper.new(...)[*]
即做一些特殊的事情。它本质上是根据当前数据创建一个新的包装器。
## Field.rakumod\nuse NativeCall;\n\n# C side\nmy constant LIB = "./libfield.so";\nclass CField is repr("CStruct") {...}\n\nsub make_field(Pointer[void], size_t --> CField) is native(LIB) { * };\nsub free_field(CField) is native(LIB) { * };\n\nclass CField {\n has Pointer $.data;\n has size_t $.size;\n\n submethod DESTROY {\n free_field(self);\n }\n}\n\n# Raku side\nclass Wrapper is export {\n has CField $.field;\n has Int $.size;\n\n # New from Raku data\n multi method new(@data) {\n self.bless:\n field => make_field(nativecast(Pointer, CArray[int32].new(@data)), @data.elems),\n size => @data.elems;\n }\n\n # New from already Wrapper\n multi method new(Wrapper $other) {\n self.bless:\n field => make_field($other.field.data, $other.size),\n size => $other.size;\n }\n\n method as-list {\n # to see what is inside\n my $nat = nativecast(CArray[int32], $!field.data);\n $nat[^$!size].List\n }\n\n multi postcircumfix:<[ ]>(Wrapper:D $w, Whatever) is export {\n # new Wrapper object around $w, essentially copies it\n Wrapper.new($w)\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n现在重现失败:
\n$ tree\n.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Field.rakumod\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 field.c\n\n$ gcc -std=c99 -Wall -fPIC -shared -olibfield.so field.c\n\n$ raku -I. -MField -e\'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"\'\nno error\n\n$ raku -I. -MField -e\'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"\'\n446: $(38717712, 0, 3)\n in block <unit> at -e line 1\n
Run Code Online (Sandbox Code Playgroud)\n有时它会像预期的那样以“无错误”结束。但有时它会像我们看到的那样死去。有趣的是,如果我DESTROY
在 CField 中注释方法,它总是“没有错误”。但内存泄漏对吗?那么我做错了什么以及如何解决它?
$ raku -v # Ubuntu 20.04\nWelcome to Rakudo\xe2\x84\xa2 v2023.09.\nImplementing the Raku\xc2\xae Programming Language v6.d.\nBuilt on MoarVM version 2023.09.\n
Run Code Online (Sandbox Code Playgroud)\n