最近在搞Compiler的CodeGenerator實驗,有一部分需要把Java程式翻譯成C程式,比如:
複製代碼 代碼如下:
int [] array;
array = new int[10];
System.out.println(array.length); //10
這段代碼翻譯成C很自然的想法是:
複製代碼 代碼如下:
int * array; // int array[] not support in C
array = (int*)malloc(sizof(int)*10);
printf("%d\n",sizof(array)/sizeof(int)); // 1
但很可惜這樣是錯誤的,因為malloc操作在堆上分配空間,不一定是連續的,sizof(array)得到的是指標本身所佔的單元,和sizeof(int)相等,無法通過sizof求得數組長度。它和下面還不一樣:
複製代碼 代碼如下:
int array[10];
printf("%d\n",sizof(array)/sizeof(int)); // 10
這裡array是數組,是指向整個連續儲存空間的常量,所以sizeof對其操作求得的是整個地區的長度。但是當數組名作為函數的參數傳遞時,數組就退化為指標,又回到了剛才問題。
我們應該怎麼做?
在 StackOverflow 搜了一下,發現ANSI C根本沒有直接辦法通過指向記憶體的指標求得分配長度。但Windows下提供了計算指標指向的記憶體大小的方法[malloc.h]:
_msize : returns the size (in bytes) as an unsigned integer.
複製代碼 代碼如下:
size_t _msize(
void *memblock
);
但由於作業系統策略的原因,實際分配到的大小可能會比指定的大一些.
在Linux下,指標往前位移一個整形大小的單元也會記錄實際分配的大小,我們來窺探一下那個單元的內容:
複製代碼 代碼如下:
//test.c
int main(){
int * p;
int i;
int size;
for (i=1;i<11;i++)
printf("%d ",i);
printf("\n");
for (i=0;i<10;i++){
p = (int*)malloc(sizeof(int)*i);
size = *(int*)((char*)p-sizeof(int));
printf("size:%d ",size);
free(p);
}
printf("\n");
}
$gcc test.c
$./a.out
1 2 3 4 5 6 7 8 9 10
17 17 17 17 25 25 33 33 41 41
看來Linux的分配策略不能使得記憶體大小和元素個數一一對應,此法不可用。 後來發現在Linux下原來也有類似_msize的函數[malloc.h]:
複製代碼 代碼如下:
int * array;
int size;
array = (int*)malloc(sizof(50);
size = malloc_usable_size(array);
printf("%d\n",size);//50
但是malloc.h不屬於標準C,我們還要繼續尋找通用之法。經過大量查閱,終於發現了一種code trick,稱作struct-hack. 前面提到過,在C語言中,int a[]是違法的,但是把它作為struct的最後一個成員卻是可以的:
複製代碼 代碼如下:
typedef struct array{
int size;
int free;
int buf[];
}array,*Tiger_array;
這是在C語言的後期加入的特性,目的就是為了實現flexible array, 這樣每次給數組分配空間時,需要同步記錄size大小。而求size的時候,直接取出來即可:
複製代碼 代碼如下:
Tiger_array ta;
ta = (int*)malloc(sizeof(array)+100);
ta->size = 100;
ta->free = 0;
需要注意一點,這時分配的大小應該是sizeof(struct)加上需求的數組大小。
這個問題就說到這裡。