“Steensgaard分析用存储形状的等价类近似可能地址,允许把多个目标合成一个代表,以获得近线性的求解成本。它与Andersen包含分析一样通常是流不敏感、上下文不敏感;差别在于目标关系的…”
形式陈述
Andersen分析用包含约束近似指针可能指向的地址。固定一个指针核心语言,语句规范化为取地址、复制、一次解引用读写和分配;不允许从任意整数伪造地址。具体读写按存储语义执行。
静态分析将每个变量存储位置和每个堆分配站点看作一个抽象单元,构成有限集合Cell。同一分配站点的许多运行时对象被合并;基础版本不区分语句执行位置或调用上下文。对每个单元x,
四类语句生成以下约束:
| 语句 | 约束 |
|---|---|
x=&a |
|
x=y |
|
x=*y |
对每个 |
*x=y |
对每个 |
x=new@h 给出
求全部约束的最小解。只向集合添加目标,不因后面的赋值覆盖而删除,因为流不敏感结果要同时覆盖所有执行时刻。
直觉
每条包含边都保留信息流方向:x=y让y的可能目标流到x,却不要求x原有的目标也反向流到y。解引用则先问指针可能指到哪些单元,再为这些单元建立新的传播关系。
所以约束图不一定一次建完。刚发现a属于pt(y)时,x=*y才暴露出新的边
例子与边界
写穿一层指针,再读回来
考虑
p = &a
q = &b
r = p
*r = q
s = *p
这里a、b也是有地址的存储单元。起初各pt集合为空,取地址先产生pt(p)=
因为a在pt(r)中,存储 *r=q 激活包含边 s=*p 激活边
pt(b)仍为空;程序没有向单元b写入其他地址。不要把“q指向b”误读成“b指向q”,也不要把pt(a)与地址a自身混为一层。
新边必须携带旧事实
若先求得pt(q)=
流不敏感会保留已经覆盖的目标
x = &a
x = &b
y = x
本分析给pt(x)=pt(y)=
相反,若程序为 p=&a; q=&b; r=p; r=q,包含分析保留pt(p)=
推论与应用
一套有界工作队列
维护每个x的目标集合及包含后继列表。发现新事实
若程序大小为n,抽象单元及规范化语句数量均为
可靠性来自抽象解释式的不变量:每次具体存储中出现的有效指针边,在对应抽象单元的pt集合中都有代表。取地址直接加入代表;复制用包含传播;load/store按所有可能目标覆盖实际被访问的那个单元。将这些局部步骤归纳到执行,就得到全局may-points-to覆盖。
最小约束解不等于最精确的全部流不敏感语义。路径相关性、不同指针事实能否同时成立、语句规范化引入的临时值,仍可能造成额外近似。可靠的共同目标是不能漏真实地址,不是保证每条静态边都能在某次执行实现。
字段敏感扩展可用
对象敏感分析进一步按接收者身份区分方法中的变量与参数传播。字段敏感区分“同一对象的哪个字段”,接收者上下文区分“同一方法正在处理哪个抽象对象”;两者可以组合,却不是同一项精度选择。
练习与解答
在主例最后增加 t=*r,能否断定pt(t)只有b?答案可以:pt(r)只有a,故新增边 r=&c 和 c=&d,则pt(r)扩大为
参考资料
- Lars Ole Andersen, Program Analysis and Specialization for the C Programming Language, DIKU博士论文,1994:包含式points-to分析
- Anders Møller and Michael I. Schwartzbach, Static Program Analysis,2026年8月版,§§11.1–11.3:分配站点抽象、四类约束及按需生成边的三次求解器