C là một ngôn ngữ cấp thấp, gần như một trình biên dịch di động, do đó cấu trúc dữ liệu và cấu trúc ngôn ngữ của nó gần với kim loại (cấu trúc dữ liệu không có chi phí ẩn - ngoại trừ các giới hạn đệm, căn chỉnh và kích thước được áp đặt bởi phần cứng và ABI ). Vì vậy, C thực sự không có gõ động tự nhiên. Nhưng nếu bạn cần nó, bạn có thể chấp nhận một quy ước rằng tất cả các giá trị của bạn là tổng hợp bắt đầu bằng một số thông tin loại (ví dụ: một số enum
...); sử dụng union
-s và (đối với những thứ giống như mảng) thành viên mảng linh hoạt trong việc struct
chứa cả kích thước của mảng.
(khi lập trình trong C, đó là trách nhiệm của bạn để xác định, tài liệu, và làm theo ước hữu ích - điều kiện và bất biến đáng chú ý trước và hậu; cũng cấp phát bộ nhớ động C đòi hỏi expliciting ước về việc ai nên free
một số heap- malloc
khu ated bộ nhớ)
Vì vậy, để đại diện cho các giá trị mà là các số nguyên đóng hộp, hoặc các chuỗi, hoặc một số loại Scheme -like biểu tượng , hoặc vectơ các giá trị, bạn sẽ khái niệm sử dụng một công đoàn được gắn thẻ (thực hiện như một sự kết hợp của con trỏ) -always bắt đầu bằng việc loại loại -, ví dụ:
enum value_kind_en {V_NONE, V_INT, V_STRING, V_SYMBOL, V_VECTOR};
union value_en { // this union takes a word in memory
const void* vptr; // generic pointer, e.g. to free it
enum value_kind_en* vkind; // the value of *vkind decides which member to use
struct intvalue_st* vint;
struct strvalue_st* vstr;
struct symbvalue_st* vsymb;
struct vectvalue_st* vvect;
};
typedef union value_en value_t;
#define NULL_VALUE ((value_t){NULL})
struct intvalue_st {
enum value_kind_en kind; // always V_INT for intvalue_st
int num;
};
struct strvalue_st {
enum value_kind_en kind; // always V_STRING for strvalue_st
const char*str;
};
struct symbvalue_st {
enum value_kind_en kind; // V_SYMBOL
struct strvalue_st* symbname;
value_t symbvalue;
};
struct vectvalue_st {
enum value_kind_en kind; // V_VECTOR;
unsigned veclength;
value_t veccomp[]; // flexible array of veclength components.
};
Để có được kiểu động của một số giá trị
enum value_kind_en value_type(value_t v) {
if (v.vptr != NULL) return *(v.vkind);
else return V_NONE;
}
Đây là một "diễn viên động" cho vectơ:
struct vectvalue_st* dyncast_vector (value_t v) {
if (value_type(v) == V_VECTOR) return v->vvect;
else return NULL;
}
và một "bộ truy cập an toàn" bên trong các vectơ:
value_t vector_nth(value_t v, unsigned rk) {
struct vectvalue_st* vecp = dyncast_vector(v);
if (vecp && rk < vecp->veclength) return vecp->veccomp[rk];
else return NULL_VALUE;
}
Thông thường bạn sẽ xác định hầu hết các chức năng ngắn ở trên như static inline
trong một số tệp tiêu đề.
BTW, nếu bạn có thể sử dụng trình thu gom rác của Boehm, thì bạn có thể mã hóa khá dễ dàng theo một số kiểu cấp cao hơn (nhưng không an toàn) và một số trình thông dịch Scheme được thực hiện theo cách đó. Một constructor vector matrixdic có thể là
value_t make_vector(unsigned size, ... /*value_t arguments*/) {
struct vectvalue_st* vec = GC_MALLOC(sizeof(*vec)+size*sizeof(value));
vec->kind = V_VECTOR;
va_args args;
va_start (args, size);
for (unsigned ix=0; ix<size; ix++)
vec->veccomp[ix] = va_arg(args,value_t);
va_end (args);
return (value_t){vec};
}
và nếu bạn có ba biến
value_t v1 = somevalue(), v2 = otherval(), v3 = NULL_VALUE;
bạn có thể xây dựng một vectơ từ chúng bằng cách sử dụng make_vector(3,v1,v2,v3)
Nếu bạn không muốn sử dụng công cụ thu gom rác của Boehm (hoặc thiết kế công cụ dọn rác của riêng bạn), bạn nên hết sức cẩn thận trong việc xác định các hàm hủy và ghi lại ai, làm thế nào và khi nào bộ nhớ phải là free
-d; xem ví dụ này Vì vậy, bạn có thể sử dụng malloc
(nhưng sau đó kiểm tra chống lại sự thất bại của nó) thay vì GC_MALLOC
ở trên nhưng bạn cần xác định cẩn thận và sử dụng một số hàm hủyvoid destroy_value(value_t)
Điểm mạnh của C là ở mức độ đủ thấp để tạo mã như trên có thể và xác định các quy ước của riêng bạn (cụ thể là phần mềm của bạn).