【问题标题】:why to use an underscore for a struct in c?为什么在 c 中为结构使用下划线?
【发布时间】:2017-10-16 16:17:26
【问题描述】:

在下面的源代码中,有人可以解释一下理性(为什么 typedef 结构 _lwm2m_object_t 使用新名称 lwm2m_object_t 是良好的编程习惯?它所做的只是去掉下划线?为什么是下划线在第一个实例中使用?

typedef struct _lwm2m_object_t lwm2m_object_t;

typedef uint8_t (*lwm2m_read_callback_t) (lwm2m_uri_t * uriP, char ** bufferP, int * lengthP, lwm2m_object_t * objectP);
typedef uint8_t (*lwm2m_write_callback_t) (lwm2m_uri_t * uriP, char * buffer, int length, lwm2m_object_t * objectP);
typedef uint8_t (*lwm2m_execute_callback_t) (lwm2m_uri_t * uriP, char * buffer, int length, lwm2m_object_t * objectP);
typedef uint8_t (*lwm2m_create_callback_t) (lwm2m_uri_t * uriP, char * buffer, int length, lwm2m_object_t * objectP);
typedef uint8_t (*lwm2m_delete_callback_t) (uint16_t id, lwm2m_object_t * objectP);
typedef void (*lwm2m_close_callback_t) (lwm2m_object_t * objectP);


struct _lwm2m_object_t
{
    uint16_t                 objID;
    lwm2m_list_t *           instanceList;
    lwm2m_read_callback_t    readFunc;
    lwm2m_write_callback_t   writeFunc;
    lwm2m_execute_callback_t executeFunc;
    lwm2m_create_callback_t  createFunc;
    lwm2m_delete_callback_t  deleteFunc;
    lwm2m_close_callback_t   closeFunc;
    void *                   userData;
};

【问题讨论】:

  • 它不再需要在使用该类型的任何地方使用“结构”。下划线是为了防止非 typedef 的名字和 typedef 的名字冲突
  • 我认为这不是一个好主意。 A)这是一个标准违规。文件范围和标记命名空间中以下划线开头的所有标识符都是保留的,B)它不必要地乱扔你的全局命名空间。不必要,因为您可以为 struct 标记和 typedef (typedef struct foo foo;) 使用相同的名称——C++ 甚至内置到语言中的约定。
  • “文件范围内的所有标识符和以下划线开头的标签命名空间都是保留的” - 你知道吗?不完全是。
  • @Bathsheba port70.net/~nsz/c/c11/n1570.html#7.1.3 第二个要点
  • 这说明了为什么你永远不应该相信互联网上的任何东西 ;-)

标签: c struct standards typedef


【解决方案1】:

就其本身而言,前导下划线没有内在含义,它只是为标签名称与 typedef 名称创建一个不同的标识符。您可以使用

获得相同的结果
typedef struct foo fooType;

标签名与普通标识符占用不同的命名空间,所以可以写

typedef struct foo foo;

有些人认为这是令人困惑的。我不知道 - 标签名称因 struct(或 unionenum)关键字的存在而消除歧义。

您应该在标识符中使用前导下划线 - 具有两个前导下划线或前导下划线后跟一个大写字母的标识符始终保留用于实现以供任何使用,而具有单个前导的标识符下划线保留在普通和标记名称空间中。

你可以使用尾随下划线来达到同样的效果

typedef struct foo_ foo;

让每个人都满意。

【讨论】:

    【解决方案2】:

    其实如果你用

    typedef struct lwm2m_object_t lwm2m_object_t;
    
    struct lwm2m_object_t {
    //
    }
    

    C 仍然允许。这是因为结构名称和 typedef 标识符具有不同的命名空间。有关 C 中命名空间的信息,请参阅 Why doesn't ANSI C have namespaces?

    但是,许多专业用户会避免这种情况,并为结构和 typedef 使用不同的名称。 MISRA1 也不允许这样做。

    规则 5.6(建议):一个名称空间中的标识符不应与另一个名称空间中的标识符具有相同的拼写,除了 结构成员和联合成员名称。 [MISRA C 2004]

    下划线的使用只是一些人遵循的惯例。您可以遵循其他约定,例如

    typedef struct sTag_lwm2m_object_t lwm2m_object_t;
    
    struct sTag_lwm2m_object_t {
    //
    }
    

    1https://en.wikipedia.org/wiki/MISRA_C

    【讨论】:

    • 也许包含指向 MISRA 的链接?
    • 我无法抗拒。添加了引用,并进行了投票。
    • 如果从 C++ 中包含,也会导致头文件失败。
    【解决方案3】:

    typedef struct _lwm2m_object_t lwm2m_object_t;意味着当你想引用struct类型时不需要写更冗长的struct _lwm2m_object_t,但是你可以直接使用lwm2m_object_t来代替。它可以节省打字并且可以使源代码更清晰。

    在实际的 struct 名称前加上一个 _ 是多年来逐渐形成的惯例。许多人使用_t 作为后缀来表示一个类型

    但有几点需要注意:

    1. 永远不要使用 下划线,因为这样做的行为是未定义的。

    2. 不要以单个下划线后跟大写字母开头。同样,此类类型名称是保留的。

    3. POSIX 明确禁止使用 _t 结束自己的类型,但请注意,标准C 允许这样做。

    许多有经验的 C 程序员发现 (3) 令人讨厌并忽略它。 (我愿意。)

    【讨论】:

    • 老实说,双下划线约定也是可恶的。许多 C 程序员也忽略了那个。我想,对便携性的关注程度不同。
    • @StoryTeller:我认为这很调皮,因为它可能与您的 C 标准库的实现在闲暇时定义的宏发生冲突。 (我在我的代码库中不允许它。)
    • 如果诚实地看待它,与标准库发生冲突的可能性并不比与 POSIX 发生冲突更大。我非常怀疑__mycompany_mymodule_mycomponent_hiddenfeature 会与标准库中的任何内容发生冲突。
    • 这是一个有趣的观点。对我来说,你应该永远编写标准 C 未定义的代码。但是,有趣的是,源自 POSIX 和 MISRA 之类的理想主义可以被视为建议,或被视为学究的沉思.
    • 是的。但这些禁令的根本原因是相同的。那些标准想要面向未来的用户代码。虽然我完全同意你对双下划线的看法,但我发现忽略 POSIX 是完全任意的(如果一个 正在构建或可能为 POSIX 系统构建)。一开始就遵守这样的约定可以使某些过渡更加顺利。所以,也许他们并不那么迂腐。只是我的 2 便士值得意见。
    【解决方案4】:

    前导下划线用于每个人或团队的不同目的。阅读此answer 中的更多信息,特别是专用于 的段落。

    我经常看到这个:

    typedef struct lwm2m_object lwm2m_object_t;
    

    _t 附加到结构的名称,向读者表明这是一个typedef'ef 名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-24
      • 1970-01-01
      • 2014-09-28
      • 2020-06-25
      • 2017-01-30
      • 2016-10-26
      • 2013-02-12
      • 2019-10-06
      相关资源
      最近更新 更多