C11 标准规定如下(6.2.4p2):
对象的生命周期是程序执行期间保证为其保留存储的部分。一个对象存在,有一个不变的地址,并在其整个生命周期中保留其最后存储的值。如果对象在其生命周期之外被引用,则行为未定义。当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。
不确定值的定义是 (3.19.2):
未指定的值或陷阱表示
我对这个限制有点问题。我发现如果复制不确定的指针是可以接受的,那么双向链表的小优化是可能的。优化依赖于以下不变量:
- 列表中头元素的
prev
指针是不确定的。 - 对于双端列表,
tail
空列表的指针是不确定的。
其他一切都与正常的双向链表相同。有了这些不变量,可以稍微优化插入到前面和从前面取出的操作。特别是(代码仅用于单端列表):
insertToFront(e)
{
e->next = head;
e->prev = NULL;
if (head) {
head->prev = e;
}
head = e;
}
insertToFrontOptimized(e)
{
e->next = head;
if (head) {
head->prev = e;
}
head = e;
}
removeFront()
{
head = head->next;
if (head) {
head->prev = NULL;
}
}
removeFrontOptimized()
{
head = head->next;
}
其他操作仍然可以工作。例如:
remove(e)
{
if (e != head) {
e->prev->next = e->next;
} else {
head = e->next;
}
if (e->next) {
e->next->prev = e->prev; // problem here
}
}
上面这行是有问题的,因为当移除 head 元素时,它会将prev
新 head 的指针设置为不确定的值。例如,新prev
指针可以是 a 之后的未初始化指针的副本insertToFrontOptimized()
,或者它可以指向 a 之后的已释放元素removeFrontOptimized()
。
但这仅在 C 语言方面存在问题;列表不变量不会被破坏,因为prev
允许新头元素的指针具有不确定的值。
对使用不确定指针的限制是否会阻止这种优化?有办法吗?