用宏实现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:如果你不嫌麻烦,可以再写个宏,把原来的变量名找回来,然后……

  我还是不搞了。

  

时间: 2024-07-28 12:56:26

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

calculate the number of characters-统计文件中的字符数,非空白字符数,字母数,输入到文件和屏幕:

//calculate the number of characters-统计文件中的字符数,非空白字符数,字母数,输入到文件和屏幕: #include<iostream> #include<fstream> #include<cstdlib> #include<cmath> int main() { using namespace std; ifstream fin; ofstream fout; double ch1 = 0,ch2 = 0,letter

ZZUOJ 10507: 非完美数

题目链接:http://acm.zzu.edu.cn:8000/problem.php?id=10507 题目大意:求L到R区间内所有数与小于它自己的约数的差的绝对值的和. 解题思路:拿个筛子筛一筛.注意1的时候还是1以及long long 代码: const int maxn = 1e6 + 5; typedef long long ll; int l, r; ll a[maxn], sum[maxn]; void dowork(){ memset(a, 0, sizeof(a)); for(

非接触数(杭州电1999)

/*非接触数 Problem Description s(n)它是一个正整数n真正的系数总和,小于n可分割n因子.例如s(12)=1+2+3+4+6=16.无论假设 号码m,s(m)不等于n,声称n非接触数. Input 包括多组数据,首先输入T,表示有T组数据.每组数据1行给出n(2<=n<=1000)是整数. Output 假设n是不可摸数,输出yes,否则输出no Sample Input 3 2 5 8 Sample Output yes yes no */ /*用打表法将 n的真因子

非指针 复制对象和数组的两种方法

JS在复制对象的时候,复制的是指针.有复制关系的两个对象,一个改变时另一个的值也跟着变了.数组也是对象,也存在这种现象. 实现对象的非指针复制的最简单方法: var obj2 = JSON.parse(JSON.stringify(obj1)); 原理就是利用JSON方法,先将对象obj1转成字符串,再解析为对象赋值给obj2.这样obj1和obj2就指向内存中的不同对象了,虽然值一样,但是检测 obj1==obj2 得到 false . 题外话:对象和数组的相等判断,得到的结果不是字面量一不一

非旋 treap 结构体数组版(无指针)详解,有图有真相

非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 所谓随机数维护平衡就是给每个节点一个随机值 key (下文中没有加随机的就代表是真实权值), 然后整棵树中 key 值要满足小(大)根堆的性质(也就是heap), 同时也要满足平衡树(tree)的性质(也就是每个节点左子树内节点真实

hdu 3887 Counting Offspring(DFS序【非递归】+树状数组)

题意: N个点形成一棵树.给出根结点P还有树结构的信息. 输出每个点的F[i].F[i]:以i为根的所有子结点中编号比i小的数的个数. 0<n<=10^5 思路: 方法一:直接DFS,进入结点x时记录一下比x小的数的个数.出来x时记录一下比x小的数的个数.相减就是F[x].结合树状数组. 方法二:写下DFS序.对DFS序列建线段树.然后从小到大对结点进行插入.用线段树统计. 代码:(方法一) int const N=1e5+5; int n,p; vector<int> G[N];

数制之非十进制数转十进制数

二进制转成十进制方法按权相加法:数码与权值相乘,然后将积相加1010101上面这个二进制数转成十进制数是首先,先展开从右到左公式数码 乘以 基数的数码位数减1次幂(12零次方 ) +(02一次方)+(12二次方)+(02的4 - 1方)+(12的5-1)+(02的6-1方)+(1*2的6-1方) 八进制转成十进制数码与权值相乘,然后将积相加3243数码与基数的数码位数-1次幂相乘38的4-1次方 + 28 的 3-1次方 + 48 的2-1次方 38的1-1次方 十六进制转成十进制数码与权值相乘

编写优质嵌入式C程序(转)

前言:这是一年前我为公司内部写的一个文档,旨在向年轻的嵌入式软件工程师们介绍如何在裸机环境下编写优质嵌入式C程序.感觉是有一定的参考价值,所以拿出来分享,抛砖引玉. 转载请注明出处:http://blog.csdn.net/zhzht19861011/article/details/45508029 摘要:本文首先分析了C语言的陷阱和缺陷,对容易犯错的地方进行归纳整理:分析了编译器语义检查的不足之处并给出防范措施,以Keil MDK编译器为例,介绍了该编译器的特性.对未定义行为的处理以及一些高级

JS || &amp;&amp;

var a = 2; var b = 3; var andflag = a && b ; var orflag = a || b; 问andflag 和orflag 分别是什么? 起初我认为: andflag 和orflag 的值都为 true; 毕竟 && 和 || 都是求Boolean ,后来发现,我错了. 答案应该是  andflag  = 3,orflag = 2; 原因是这样的: 在运算过程中,首先js  会将 && 和||  两边的值转成Bool