编译为C的语言中的智能指针



我正在编写一种可编译为C的简单语言,我想实现智能指针。不过,我需要一些帮助,因为我似乎想不出该如何解决,或者这是否可能。我目前的想法是在指针超出范围时释放指针,编译器将处理插入释放。这引出了我的问题:

  • 当指针超出范围时,我该如何判断
  • 这可能吗

编译器是用C编写的,并编译到C。我想我可以在编译时检查指针何时超出范围,并在为指针生成的代码中插入一个free,即:

// generated C code.
int main() {
    int *x = malloc(sizeof(*x));
    *x = 5;
    free(x); // inserted by the compiler
}

范围规则(在我的语言中)与C.完全相同

我目前的设置是您的标准编译器,首先它对文件内容进行词法分析,然后解析令牌流,对其进行语义分析,然后生成C代码。解析器是递归下降解析器。我希望避免在执行时发生一些事情,也就是说,我希望它是一个编译时检查,几乎没有开销,并且不是完全的垃圾收集。

对于函数,每个{启动一个新的作用域,每个}关闭相应的作用域。当达到}时,该块内的变量将超出范围。当结构实例超出作用域时,结构的成员超出作用域。有几个例外,比如临时对象在下一个;超出了作用域,编译器将for循环静默地放在自己的块作用域内。

struct thing {
   int member;
};
int foo;
int main() {
   thing a;
   {
       int b = 3;
       for(int c=0; c<b; ++c) {
          int d = rand(); //the return value of rand goes out of scope after assignment
       } //d and c go out of scope here
   } //b goes out of scope here
}//a and its members go out of scope here
//globals like foo go out-of-scope after main ends

C++试图真的以相反的顺序破坏对象,你可能也应该在你的语言中这样做。

(这都是我对C++的了解,所以它可能与C略有不同,但我不认为是)


至于记忆,你可能会想在幕后施展一点魔法。每当用户对内存进行mallocs时,您都会将其替换为分配更多内存的东西,并在额外的空间中"隐藏"引用计数。在分配开始时最容易做到这一点,并且为了保持一致性保证,您可以使用类似于以下的东西:

 typedef union {
     long double f;
     void* v;
     char* c;
     unsigned long long l;
 } bad_alignment;
 void* ref_count_malloc(int bytes)
 {
     void* p = malloc(bytes + sizeof(bad_alignment)); //does C have sizeof?
     int* ref_count = p;
     *ref_count = 1; //now is 1 pointer pointing at this block
     return p + sizeof(bad_alignment);
 }

当他们复制指针时,你会在复制之前无声地添加类似的内容

 void copy_pointer(void* from, void* to) {
     if (from != NULL) 
         ref_count_free(free); //no longer points at previous block
     bad_alignment* ref_count = to-sizeof(bad_alignment);
     ++*ref_count; //one additional pointing at this block
 }

当它们空闲或指针超出范围时,您可以添加/替换如下调用:

 void ref_count_free(void* ptr) {
     if(ptr) {
         bad_alignment* ref_count = ptr-sizeof(bad_alignment);
         if (--*ref_count == 0) //if no more pointing at this block
             free(ptr);
      }         
 }

如果你有线程,你就必须为所有这些添加锁。我的C已经生疏,代码也未经测试,所以对这些概念做了大量的研究。

这个问题稍微困难一些,因为您的代码很简单,但是。。。如果另一个指针指向与x相同的位置,该怎么办?

// generated C code.
int main() {
    int *x = malloc(sizeof(*x));
    int *y = x;
    *x = 5;
    free(x); // inserted by the compiler, now wrong 
}

毫无疑问,您将拥有一个堆结构,其中每个块都有一个头,告诉a)块是否正在使用,b)块的大小。这可以通过一个小的结构来实现,或者通过在b)的整数值中使用a)的最高位来实现[这是64位编译器还是32位?]。为了简单起见,让我们考虑一下:

typedef struct {
    bool allocated: 1;
    size_t size;
} BlockHeader;

您必须向这个小结构添加另一个字段,这将是一个引用计数。每当指针指向堆中的那个块时,就会增加引用计数。当指针停止指向块时,其引用计数就会递减。如果它达到0,那么它可以被压缩或其他什么。allocated字段的使用现已结束。

typedef struct {
    size_t size;
    size_t referenceCount;
} BlockHeader;

引用计数实现起来很简单,但也有缺点:这意味着每次指针值发生变化时都会有开销。尽管如此,这是最简单的工作方案,这就是为什么一些编程语言仍然使用它,比如Python。

相关内容

  • 没有找到相关文章

最新更新