C11标准规定如下(6.2.4p2):
对象的生命周期是程序执行期间为其保留存储空间的部分。对象在其生命周期内存在,具有恒定的地址,并保留其上次存储的值。如果对象在其生命周期外被引用,则行为未定义。当指向的对象(或刚好在其之后)到达其生命周期终点时,指针的值变得不确定。
未确定值的定义是(3.19.2):
既可以是未指定的值,也可以是陷阱表示。
我遇到了这个限制的问题。如果接受复制未确定指针,那么双向链表的小优化就可能实现。该优化依赖于以下不变量:
头元素的prev指针是不确定的。
对于双端列表,空列表的tail指针是不确定的。
除此之外,其他所有内容与普通的双向链表相同。利用这些不变量,可以轻微地优化从前面插入和删除的操作。特别是(代码仅适用于单向列表):
上面的代码存在问题,因为在删除头元素时,它会将新头部的
但是这只在C语言方面存在问题;列表不变式并没有被破坏,因为允许新头元素的
禁止使用不确定指针的限制是否阻止了这种优化?有没有绕过的方法?
对象的生命周期是程序执行期间为其保留存储空间的部分。对象在其生命周期内存在,具有恒定的地址,并保留其上次存储的值。如果对象在其生命周期外被引用,则行为未定义。当指向的对象(或刚好在其之后)到达其生命周期终点时,指针的值变得不确定。
未确定值的定义是(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
}
}
上面的代码存在问题,因为在删除头元素时,它会将新头部的
prev
指针设置为一个不确定的值。例如,在insertToFrontOptimized()
之后,新的prev
指针可能是未初始化指针的副本,或者在removeFrontOptimized()
之后它可能指向被释放的元素。但是这只在C语言方面存在问题;列表不变式并没有被破坏,因为允许新头元素的
prev
指针具有不确定的值。禁止使用不确定指针的限制是否阻止了这种优化?有没有绕过的方法?
head->prev
指向垃圾的问题是什么?你永远不会读取head->prev
,你只会写入它。 - Porkbutts