在C语言中复制不定指针
C11标准规定如下(6.2.4p2): 对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义指针的值变为 不确定其指向(或刚刚过去)的对象何时到达 生命周期结束。 不确定值的定义为(3.19.2): 未指定的值或陷阱表示法 我对这个限制有点问题。我发现,如果复制不确定指针是可以接受的,则可以对双链表进行小规模的优化。优化依赖于以下不变量:在C语言中复制不定指针,c,pointers,undefined,undefined-behavior,C,Pointers,Undefined,Undefined Behavior,C11标准规定如下(6.2.4p2): 对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义指针的值变为 不确定其指向(或刚刚过去)的对象何时到达 生命周期结束。 不确定值的定义为(3.19.2): 未指定的值或陷阱表示法 我对这个限制有点问题。我发现,如果复制不确定指针是可以接受的,则可以对双链表进行小规模的优化。优化依赖于以下不变量: 列表中head元素的prev指
- 列表中head元素的
指针不确定prev
- 对于双端列表,空列表的
指针是不确定的尾部
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元素时,它会将新head的prev
指针设置为一个不确定的值。例如,新的prev
指针可以是insertToFrontOptimized()
之后未初始化指针的副本,也可以指向removeffrontoptimized()
之后释放的元素
但这只是C语言的问题;列表不变量不会被破坏,因为允许新head元素的prev
指针具有不确定的值
对使用不确定指针的限制是否阻止了这种优化?有办法吗?如果我没弄错的话,你担心的是线路问题
e->next->prev = e->prev; // problem here
调用未定义的行为,因为执行时,e->prev
可能是不确定的
担心这一点可能是好的
如果我是你,我会用这句话来代替我自己:
memcpy(&e->next->prev, &e->prev, sizeof(e->prev);
它可能会被编译成与原始赋值相同的指令。唯一的缺点是,根据memcpy()
的规范,&e->next->prev
和&e->prev
在调用时必须是不同的,而赋值e->next->prev=e->prev代码>允许它们完全重叠
也就是说,这是一个有C的形式语义来回答的问题,而我所知道的形式语义(KCC,CompCert,…)都允许您将一个不确定变量复制到另一个完全相同类型的变量上。为什么需要触摸未定义的指针?考虑这个变型:
remove(e)
{
if (e != head) {
e->prev->next = e->next;
if (e->next) {
e->next->prev = e->prev; // NO problem here
}
} else {
head = e->next;
// OK, new head->prev is undefined now. Who cares?
}
}
但是head->prev
指向垃圾有什么问题?你将永远不会读取head->prev
,你只会对它进行写入。问题是第一个元素的remove()将读取head->prev并将新head的->prev设置为该值。如果只在不删除第一个元素的情况下添加一个检查,则会降低性能。谢谢,这就是我为代码所做的,其他操作也可以像这样修复,而不需要额外的分支。多么明显:)谢谢你的信息。无论如何,我认为将指针包装在结构中也会起作用(并且可能更优雅一些,不依赖memcpy优化),因为结构保证没有陷阱表示。对不起,我被迫接受罗曼的回答,因为这就是我解决问题的方法;)
remove(e)
{
if (e != head) {
e->prev->next = e->next;
if (e->next) {
e->next->prev = e->prev; // NO problem here
}
} else {
head = e->next;
// OK, new head->prev is undefined now. Who cares?
}
}