C 比较已释放的指针';d调用UB?

C 比较已释放的指针';d调用UB?,c,undefined-behavior,hexchat,C,Undefined Behavior,Hexchat,这似乎是一种相当常见的模式,例如在hexchat中(可能无法编译,另请参见。还要注意,hexchat\u plugin\u get\u info从未被永远使用过,因此为了简单起见,我省略它): 这是一种事情吗?在指向的对象到达其生命周期结束后使用指针值是不确定的,如中所述(重点是我的): 对象的生存期是程序执行过程中的一部分 保证为其保留的存储空间。存在一个对象, 具有恒定地址,并保留其上次存储的值 在它的一生中。如果在对象的外部引用对象 在生命周期中,行为未定义指针的值变为 不确定当它指向(或

这似乎是一种相当常见的模式,例如在hexchat中(可能无法编译,另请参见。还要注意,
hexchat\u plugin\u get\u info
从未被永远使用过,因此为了简单起见,我省略它):


这是一种事情吗?

在指向的对象到达其生命周期结束后使用指针值是不确定的,如中所述(重点是我的):

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

使用它的值(仅用于任何内容)是一种明确的未定义行为,如(未定义行为)中所述:

在以下情况下,该行为未定义:[……] 使用指向其生存期已结束的对象的指针的值 (6.2.4)


是的,严格地说,使用已经为任何事情释放的指针值——即使是看似无害的比较——是未定义的行为。在实践中不太可能造成任何实际问题,但我认为这是值得避免的

另见列表。

tl;dr:在不考虑所识别对象的生存期的情况下执行某些操作(如指针上的比较)的能力是一种流行的扩展,绝大多数编译器可以配置为支持禁用优化。然而,标准并没有强制要求支持它,激进的优化器可能会破坏依赖它的代码

在编写标准时,存在一些分段内存平台,试图将指针加载到寄存器会导致系统检索有关指针所在内存区域的信息。如果此类信息不再可用,试图检索该信息可能会在本标准管辖范围之外产生任意后果。如果标准要求涉及此类指针的比较除了产生0或1之外没有任何副作用,那么该语言在此类平台上就不切实际了

尽管该标准的作者无疑意识到,能够使用与任意指针的比较(但要注意,结果可能不是特别有意义)是每个针对传统硬件的实现所支持的一个有用特性,但他们认为没有必要将其视为一个简单的工具“流行的扩展”,质量实现在这样做时支持它,这将是有用和实用的

根据C89基本原理,第11页第23行:

术语未指定行为、未定义行为和实现定义行为如下: 用于对编写属性不符合标准的程序的结果进行分类,或 无法完全描述。采用此分类的目的是允许 实现的多样性使得实现的质量成为 市场以及允许某些流行的扩展,而无需移除 符合标准。标准的资料性附录J列出了这些行为 属于这三类中的一类

不幸的是,尽管目前使用的几乎所有平台都可以以几乎零成本支持这种语义(*),一些编译器作者认为,他们的愿望是假设代码永远不会用释放的指针做任何事情,这比程序员从传统平台上基本上普遍支持的扩展中获得的任何价值都更重要。除非有人能保证任何人使用自己的代码都会禁用phony““优化”是由过度渴望的优化器的作者强加的,他们试图消除语言中有用的扩展,人们可能不得不编写额外的代码来解决缺少此类扩展的问题


(*)在某些情况下,如果函数向外部代码公开其已分配和释放的存储区域的多个指针,则必须维护行为保证这些指针将比较相等的编译器将因此需要实际执行泄漏指针的存储操作;将指针视为indeterminate将允许删除存储。但是,在人为的场景之外,使用泄漏到外部世界的指针删除此类存储所节省的成本几乎不会对性能产生任何有意义的影响。

为什么
userdata
可能会被释放?如果某些东西仍然可以访问它,那么它应该被释放直到存在。如果它被释放,那么可以在同一内存位置分配另一个对象。对任何对象使用释放指针的值都是UB。你能显示相关代码而不是粘贴链接吗?这个例子相当不清楚…什么是
ph
?在C中,与自由指针的比较是UB(除非自由指针是空指针)。即使赋值
p=freed_pointer;
也是UB。不知道这在hexchat或OP的问题中是如何应用的,这里的代码缺乏完整性。不确定比较释放的指针值是否是未定义的bahaviour,但肯定是无意义的。@Jabberwocky:在某些情况下,检查
realloc()
移动了一个块,可以避免重新计算从其地址派生的指针用于在最终大小已知后收缩块,块保持不变的场景可能比移动的场景更常见。将使用完全相同的两段来回答,但来自C17草稿。可能会添加,尽管重新分配变量是可以的,因为分配给
NUL
static hexchat_plugin *ph;
static int timer_cb(void *userdata) {
    if (hexchat_set_context(ph, userdata)) { /* <-- is this line UB? */
        /* omitted */
    }
    return 0;
}
static int do_ub(char *word[], char *word_eol[], void *userdata) {
    void *context = hexchat_get_context(ph);
    hexchat_hook_timer(ph, 1000, timer_cb, context);
    hexchat_command(ph, "close"); /* free the context - in practice this would be done by another plugin or by the user, not like this, but for the purposes of this example this simulates the user closing the context. */
    return HEXCHAT_EAT_ALL;
}
int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg) {
    *plugin_name = "do_ub";
    *plugin_desc = "does ub when you /do_ub";
    *plugin_version = "1.0.0";
    ph = plugin_handle;
    /* etc */
    hexchat_hook_command(ph, "do_ub", 0, do_ub, "does UB", NULL);
    return 1;
}
int
hexchat_set_context (hexchat_plugin *ph, hexchat_context *context)
{
    if (is_session (context))
    {
        ph->context = context;
        return 1;
    }
    return 0;
}

int
is_session (session * sess)
{
    return g_slist_find (sess_list, sess) ? 1 : 0;
}