在C语言中复制不定指针

在C语言中复制不定指针,c,pointers,undefined,undefined-behavior,C,Pointers,Undefined,Undefined Behavior,C11标准规定如下(6.2.4p2): 对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义指针的值变为 不确定其指向(或刚刚过去)的对象何时到达 生命周期结束。 不确定值的定义为(3.19.2): 未指定的值或陷阱表示法 我对这个限制有点问题。我发现,如果复制不确定指针是可以接受的,则可以对双链表进行小规模的优化。优化依赖于以下不变量: 列表中head元素的prev指

C11标准规定如下(6.2.4p2):

对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义指针的值变为 不确定其指向(或刚刚过去)的对象何时到达 生命周期结束。

不确定值的定义为(3.19.2):

未指定的值或陷阱表示法

我对这个限制有点问题。我发现,如果复制不确定指针是可以接受的,则可以对双链表进行小规模的优化。优化依赖于以下不变量:

  • 列表中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?
    }
}