代码之家  ›  专栏  ›  技术社区  ›  Dror K.

用%p打印空指针是未定义的行为?

  •  94
  • Dror K.  · 技术社区  · 9 年前

    %p 转换说明符?

    #include <stdio.h>
    
    int main(void) {
        void *p = NULL;
    
        printf("%p", p);
    
        return 0;
    }
    

    3 回复  |  直到 9 年前
        1
  •  93
  •   Oliver Charlesworth    9 年前

    证明 it:) 1.


    [7.1.4]

    除非在下面的详细描述中另有明确说明,否则以下每个语句都适用:如果函数的参数具有无效值( 例如 或空指针 , […其他示例…] [...] 行为未定义。 […其他声明…]

    这是一种笨拙的语言。一种解释是,列表中的项目对于所有库函数都是UB,除非被单个描述覆盖。但是列表以“例如”开头,表明它是说明性的,而不是详尽的。例如,它没有提及字符串的正确空终止(对于例如。 strcpy

    因此,很明显,7.1.4的意图/范围只是“无效值”导致UB( ). 我们必须查看每个函数的描述,以确定什么是“无效值”。

    示例1-

    [7.21.2.3]

    这个 拷贝字符串 函数复制 s2 (包括终止的空字符)插入 s1 。如果复制发生在重叠的对象之间,则行为未定义。

    s2

    • [7.6.4.1(fenv)] 指向的对象 通过 envp

    • [7.12.6.4(frexp)] 将整数存储在int中 指向的对象 通过 exp

    • 这个 指向的流 stream

    示例2- printf

    这是关于 %p :

    p void 。指针的值以实现定义的方式转换为一系列打印字符。

    Null是有效的指针值,本节没有明确提到Null是特例,也没有明确提到指针必须指向对象。因此,它被定义为行为。


    rationale

        2
  •  20
  •   Peter Varo    9 年前

    简短的回答

    .使用 %p 转换说明符具有未定义的行为。话虽如此,但我不知道任何现有的一致性实现会出现不当行为。


    转换说明符需要一个指针类型为void的参数,指针到可打印字符的转换由实现定义。它没有说明需要空指针。

    标准库函数的介绍指出,作为(标准库)函数参数的空指针被视为无效值,除非另有明确说明。

    C99 C11 §7.1.4 p1

    […]如果函数的参数具有无效值(例如[…]空指针,[…]则行为未定义。

    期望空指针作为有效参数的(标准库)函数示例:

    • fflush() 使用空指针刷新“所有流”(适用)。
    • freopen() 使用空指针指示与流“当前关联”的文件。
    • snprintf()
    • realloc() 使用空指针分配新对象。
    • free()
    • strtok() 在后续调用中使用空指针。

    snprintf() memcpy() , memmove() , strncpy() memset() , memcmp()

    它不仅在标准库简介中有详细说明,而且在这些功能的简介中也有详细说明:

    C99 §7.21.1 p2 C11 §7.24.1 p2

    其中一个参数声明为 size_t


    这是故意的吗?

    %p 使用空指针实际上是有意的,但由于标准明确规定空指针被视为无效值,作为标准库函数的参数,然后它明确指定了空指针是有效参数的情况(snprintf、free等),然后它再次重复要求参数即使在零‘n’的情况下也是有效的( memcpy memmove , memset ),那么我认为有理由假设C标准委员会不太关心这些未定义的东西。

        3
  •  -1
  •   supercat    8 年前

    C标准的作者没有努力详尽地列出实现必须满足的所有行为需求,以适合任何特定目的。相反,他们希望编写编译器的人能够运用一定的常识,无论标准是否需要。

    1. 对于所描述的场景,答案显然是肯定的。

    2. 一些迟钝的编译器作者是否会扩展对标准的解释,从而证明做一些奇怪的事情是合理的? 我希望不会,但不排除这种可能性。

    3. 清理编译器是否应该抱怨该行为?这取决于用户的偏执程度;

    如果对标准的合理解释意味着定义了一种行为,但一些编译器编写者扩展了解释以证明这样做是正确的,那么标准所说的真的重要吗?