用宏实现C/C++从非零整数开始的数组

  相信大家在刚学习C/C++时,都会对数组下标从0开始编号有疑惑。尽管我是喜欢从0开始编号的“0党”,但是也有很多的人是喜欢从1开始编号。

  意识到C/C++数组与指针具有一定的相似性后,我开始构思如何仿造Pascal实现类似于array[1..100]这样的声明方法。

  今天和大神gjs讨论后,他给出第一个实现方案:

int buf[100];
int a[100];
a = buf + 10;    //等价于a = &b[10]

  这样目的是访问a[0]的时候相当于访问b[10],因此a[-10]相当于访问b[0],这样我们可以通过访问a来访问buf,相当于得到一个从-10开始,长度为100的数组。

  可惜的是,编译不通过。听说是数组不能作为左值。

  因此我们改用指针实现。

  

int buf[100];
int *a = buf + 10;
a[-3] = 3;

  编译成功。

  我们决定改写成宏。

#define array(type, name, lb, ub) type buf[ub - lb + 1]; type* name = buf - lb

  这样,我们就可以使用array(int, abc, -5, 5)来声明一个下标范围为-5到5的“数组”。

  似乎是没有问题了。

  不过,遇到多个array声明就有问题了:

int main(){
    array(int, a, 1, 100);
    array(int, b, -1, 1);
}

  编译器抗议了:

10:5: error: conflicting declaration 'int buf [3]'
9:5: error: 'buf' has a previous declaration as 'int buf [100]'

  这是由于使用了两个临时变量造成的。展开定义:

int buf[100 - 1 + 1]; int* a = buf - 1;
int buf[1 - -1 + 1]; int* b = buf - -1;

  声明了两个buf!

  为了解决这一问题,我们使用这样的方法:改名。

  这是新的array声明:

  

#define array(type, name, lb, ub) type name##buf[ub - lb + 1]; type* name = name##buf - lb

  注意到##号了吗?这是连接的意思。这样,他们展开后就得到了:

int abuf[100 - 1 + 1]; int* a = abuf - 1;
int bbuf[1 - -1 + 1]; int* b = bbuf - -1;

  当然,这并不能保证没有bug。比如说我又声明了一个char abuf[30]。

  最好的方法是将它改复杂一些,再加上大量的随机字符,然后还要加上文件名、行数,将冲突的可能性改到最小。当然,故意作死的人我们也拦不住。

  好了,现在使用是一点问题都没有。但是使用memset的话……

  你可能会说“不就是不用memset(a, 0, sizeof a),而用memset(a + lb, 0, sizeof a)嘛,有什么不会的?”

  这就不对了。我们声明的a是指针,sizeof a的结果一般是4,因为指针大小是4。这样无法达到初始化的结果。

  gjs:如果你不嫌麻烦,可以再写个宏,把原来的变量名找回来,然后……

  我还是不搞了。

  

posted @ 2016-08-16 15:18  张瑯小强  阅读(700)  评论(0编辑  收藏  举报