我想在ruby中创建自己的动态数组类(作为培训)。 这个想法是要创建一个DynamicArray类,它具有一个容量(在给定时刻它可以容纳的元素数量),一个大小(在给定时刻实际被推入数组的元素数量)和一个static_array,它是固定大小的int静态数组。每当此static_array满时,我们将创建一个容量为原始static_array两倍的新静态数组,并复制新static_array内部的每个元素。 由于ruby中没有静态数组,所以我的想法是使用FFI https://github.com/ffi/ffi。在c中创建一个函数,该函数创建一个大小为n的int静态数组,然后可以在我的ruby程序中使用它。 我对C的知识很少,并且很难理解FFI的文档 到目前为止,这就是我创建的create_array.c文件,该文件定义了我的c函数以创建数组。
#include<stdio.h>
int * createArray ( int size )
{
int array[size];
return 0;
}
一个create_array.h文件(据我对FFI的了解,您需要将c函数放入c库中。):
int * createArray ( int size )
这是我的dynamic_array.rb文件,可以按照以下方式进行操作:
require 'ffi'
class DynamicArray
extend FFI::Library
ffi_lib "./create_array.h"
attach_function :create_array, [:int], :int
def initialize
@size = 0
@capacity = 1
@current_index = 0
@static_array = create_array(@capacity)
end
def add(element)
@size += 1
resize_array if @size > @capacity
@static_array[@current_index] = element
@current_index += 1
end
private
def resize_array
@capacity = @capacity*2
new_arr = create_array(@capacity)
@static_array.each_with_index do |val, index|
new_arr[index] = val
end
@static_array = new_arr
end
end
这里有一些添加和调整大小的测试:
def test_add
dynamic_arr = DynamicArray.new
dynamic_arr.add(1)
dynamic_arr.add(2)
assert_equal(1, dynamic_arr.static_array[0])
assert_equal(2, dynamic_arr.static_array[1])
end
def test_resize_array
dynamic_arr = DynamicArray.new
dynamic_arr.add(1)
dynamic_arr.add(2)
assert_equal(2, dynamic_arr.capacity)
dynamic_arr.resize_array
assert_equal(4, dynamic_arr.capacity)
assert_equal
end
你能解释一下我应该做些什么吗?
答案 0 :(得分:7)
似乎您没有正确使用C代码。
在create_array
C函数中:
malloc
(或alloc
系列中的其他函数)分配它的内存将所有内容放在一起,这就是您的create_array.c
文件的样子:
#include <stdlib.h> /* in order to use malloc */
int * create_array (int size){
int *a = malloc(size * sizeof(int));
return a; /* returning the pointer to the array a*/
}
和您的头文件create_array.h
:
int * create_array(int);
要封装所有内容,您仍然需要先进行编译,然后才能使ruby接触到它:
gcc -shared -o create_array.so -fPIC create_array.c
此命令正在使用gcc将C代码从create_array.so
源文件编译到名为create_array.c
的共享库中。需要安装gcc才能使其正常工作。
最后,您可以在ruby中使用C函数,并在dynamic_array.rb
中进行一些修改:
require 'ffi'
class DynamicArray
extend FFI::Library
ffi_lib "./create_array.so" # using the shared lib
attach_function :create_array, [:int], :pointer # receiving a pointer to the array
# rest of your code
现在,这应该可以工作! 但是您的ruby代码仍然存在一些问题:
@static_array = create_array(@capacity)
时,您将收到指向分配的数组的C指针,而不是数组本身,至少不是在ruby中。@static_array[@current_index] = element
无效NoMethodError: undefined method '[]=' for #<FFI::Pointer address=0x000055d50e798600>
void add_to_array (int * array, int index, int number){
array[index] = number;
}
attach_function :add_to_array, [:pointer, :int, :int], :void
add_to_array(@static_array, @current_index, element)
@static_array.each_with_index
也一样,您需要用C编写代码。答案 1 :(得分:1)
问题中的以下函数不分配您想要的数组:
#include<stdio.h>
int * createArray ( int size )
{
int array[size];
return 0;
}
您编写的函数中的数组对象会自动分配到堆栈上,一旦函数返回,它将自动销毁。实际上,由于不使用它,C编译器可能会优化数组。
您可能希望做的是:
VALUE * create_array(size_t size) {
VALUE * a = calloc(size, sizeof(*a));
return a;
}
现在,返回的a
是VALUE
的数组(或者从技术上讲,是指向数组第一个成员的指针)。
VALUE
是Ruby对象的C等效项(通常将映射到转换为unsigned long
的标记指针)。
调整大小可以使用realloc
,它会自动将现有数据复制到新的内存(或数组)中:
VALUE * tmp = realloc(tmp, new_size * sizeof(*a));
if(!tmp) {
/* deal with error, print message, whatever... */
free(a);
exit(-1);
}
a = tmp;
您仍然需要通过FFI将C代码连接到Ruby层,但这应该回答您有关如何调整数组大小(并纠正有关创建数组的错误)的问题。
注意:对于较大的分配,重新分配可以优化复制阶段,这在大型阵列中非常有好处,并且对性能有很大的积极影响。这可以通过利用以下事实来执行:内存地址是虚拟的,不必映射到连续的内存段。也就是说,同一内存页可能会在新分配中收到新地址。