【问题标题】:NULL macro or NULL const [duplicate]NULL 宏或 NULL const [重复]
【发布时间】:2016-05-17 07:09:15
【问题描述】:

我目前正在阅读一本关于 C++ 的书,在这本书中作者解释说最好使用常量而不是 NULL 宏,但没有真正解释原因或给出任何优点或缺点。

那么为什么最好使用:

const int NULL = 0;
int *ptr = NULL;

代替:

int *ptr = NULL;

给出的唯一解释是NULL 宏不是类型安全的。

【问题讨论】:

  • 使用 nullptr ,根据现代 c++
  • The NULL macro 首先必须是一个宏。其次,它必须是评估为零的常量右值,是实际的零或其他特定于平台的值,当转换为指针时为空指针,或者是nullptr_t prvalue(如nullptr)。该宏通常定义为0nullptr(取决于编译器使用的C++ 规范)。使用变量没有意义,vill也会导致转换问题。
  • 那些告诉你该怎么做而不说为什么的书是相当没用的。
  • 而这本书真的告诉你要定义一个名为NULL的整数常量吗?如果是这样,请不要再使用它。
  • @simon 从来没有也永远不会是一个好的建议。

标签: c++ null-pointer


【解决方案1】:

所有的都过时了。

请改用nullptr。这是一个不指向任何东西的指针的特殊值。它甚至有自己的类型std::nullptr_t

不能保证地址 0x0 对应于“未初始化”的指针值,但请注意,保证字面量 0 会转换为空指针值。

【讨论】:

  • 呃……什么?仍然保证文字 0 隐式转换为空指针。
  • 但可以保证ptr=0; 会为指针ptr 分配平台适当的空值。
  • @hvd 仅当它是一个整数类型的编译时常量表达式时(const int 全局对象不是,即使它是用文字 0 初始化的。)
  • @TheParamagneticCroissant 在这两个方面都错了。 const int 全局对象 在初始化程序可见的上下文中的编译时常量表达式,并且可以在常量表达式就足够(例如模板参数)的上下文中使用,但尽管如此,不是'不是一个空指针常量,因为规则不再是它必须是一个值为零的整数类型的编译时常量表达式,而是一个文字0
  • @hvd 文字0 仍然保证隐式转换为空指针。 我会说它是显式,给定4.10 指针转换:“空指针常量 是整数类型的整数常量表达式纯右值,其计算结果为零或std::nullptr_t 类型的纯右值。”
【解决方案2】:

如果您使用的是 C++11,那么建议使用 nullptr 而不是 NULL。

以下是 Bjarne Stroustrup 的 The C++ Programming Language

中的几行
  • 在旧代码中,通常使用 0 或 NULL 代替 nullptr(第 7.2.2 节)。但是,使用 nullptr 可以消除整数(例如 0 或 NULL)和指针(例如 nullptr)之间的潜在混淆。
  • 在不同的实现中NULL的定义存在差异;例如,NULL 可能是 0 或 0L。在 C 中,NULL 通常是 (void∗)0,这使得它在 C++ 中是非法的(第 7.2.1 节):
  • 使用 nullptr 使代码比替代代码更具可读性,并避免在函数被重载以接受指针或整数时潜在的混淆

希望能帮助你理解。

【讨论】:

    【解决方案3】:

    确实,“NULL 宏”不是类型安全的。这意味着您的编译器不知道您是否使用了正确的类型。例如使用memcpy时:

    SomeClass a;
    AnotherClass b;
    memcpy((void*)&a, (void*)&b, sizeof(b));
    

    (取自there

    编译器只能在内存中看到两个指针。但是SomeClassAnotherClass 是不完整的类型。

    正如其他人所说,如果您可以使用 C++11,请使用 nullptr

    【讨论】:

    • 这与NULL 无关,但与类型不安全的void* 通用指针类型有关。此外,在 C++ 中使用 memcpy 通常不是一个好主意。
    【解决方案4】:

    作者是正确的,预处理器宏没有类型(与变量相反,在它们的声明期间强制类型)。所以宏在这方面更危险,因为编译器无法验证表达式中的类型正确性,并且您可能会在编译期间错过重要的警告/错误消息

    当然,编译器创建者可以(并且通常会这样做,我希望)提供一个版本的 NULL 宏,它通过强制转换扩展为一个值,与 C 定义等效(不完全相同!):

    #define N ((void*)0)
    

    顺便说一句:因为 C++11 NULL 可以评估为 std::nullptr_t 类型。

    所以我不希望NULL有太多问题,但你可以考虑避免使用你自己的宏。或者至少要谨慎使用宏,只要您不能完全确定是否可以预见到您的宏可能会以各种表达式进行扩展的许多上下文。

    对于一个简短的练习,您可以尝试以下操作,以查看宏没有类型,因此它们的类型会在表达式中推导出来,例如算术转换或提升。在下面的示例中, MY_NULL 宏(=不进行强制转换)会导致非常危险的赋值。这就是你的书作者的想法,并试图警告你。

    MY_TYPED 宏评估为强制转换表达式,这确保编译器在尝试时捕获错误,例如i = MY_TYPED_NULL;

    #define MY_NULL 0
    #define MY_TYPED_NULL ((void*)0)
    int i;
    float f;
    void* v;
    i = MY_NULL;    // bad: no compiler error
    f = MY_NULL;    // bad: no compiler error
    v = MY_NULL;    // seems to match programmer's intention
    i = MY_TYPED_NULL;    // good: compiler error
    f = MY_TYPED_NULL;    // good: compiler error
    v = MY_TYPED_NULL;    // correct
    

    【讨论】:

    • 我不知道,为什么人们一直这么说。当然,NULL 之类的东西有一个类型(出于其他原因,这是令人憎恶的)。只是宏参数,可能有问题。
    • NULL 在 C++ 中一直是 0,因为这就是它在 C++ 中的工作方式。
    • @manni66:你是对的,但你只说价值。我的观点是关于 type
    • 所以?我不确定,你的练习应该证明什么。问题不在于 NULL(或者更确切地说,它计算的表达式)没有类型,而是标准允许从整数文字 0(有类型)到指针类型的隐式转换。我并不是说当有更好的选择时使用宏是一个好主意或任何东西。只是您的示例没有显示 NULL 没有类型。但我想我误解了你最初陈述的意思。
    • 引用标准 (N3242) “宏 NULL 是本国际标准 (4.10) 中实现定义的 C++ 空指针常量。”和“可能的定义包括 0 和 0L,但不包括 (void*)0。”
    猜你喜欢
    • 2015-08-02
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-07
    • 2011-12-24
    相关资源
    最近更新 更多