关于首字符下划线及C/C++语言保留的名称

C和C++语言为实现保留了特定类别的名称,这意味着您无法在自己的代码中使用它们。部分名称被无条件保留,禁止用于变量名、参数名、类名、方法名、宏名等任何场景;另一些名称仅在特定上下文中被保留。

C++ 的命名规则详见 [lex.name] 章节。C语言的规则恰好与C++规则基本一致(参见第7.1.3节 “保留标识符”),这使得规则更易于记忆。

元素周期表
模式 条件
以两个下划线开头 保留
以下划线加大写字母开头 保留
以下划线加其他字符开头 全局作用域保留(含宏)
包含两个连续下划线 C++中保留(C中允许)

需注意:若私有成员名称以大写字母开头,则常见的“成员名前缀下划线”约定将违反此规则。

class Widget
{
public:
    Widget();

private:
    int _size; // 有效
    void _Toggle(); // 无效
};

C语言不支持命名空间,因此也需在全局命名空间中预留名称以备未来扩展。某些名称不能用于具有外部链接的符号。它们可用于类型名、枚举成员、局部变量以及声明为静态存储类的函数或全局变量,但不能用于 extern 函数或 extern 全局变量。

// 不允许:外部链接标识符
// 以 “str” 和小写字母开头
int strategy;
void strafe() { /* ... */ }

// 允许:内部链接标识符以
// “str” 和小写字母开头
static int strawberry;
static void stream_video() { /* ... */ }

此外,若包含对应头文件,这些名称可被函数样式宏覆盖。这意味着若需在无外部链接的场景使用保留名,必须先通过#undef取消定义或用括号包裹名称,以避免其被视为宏。

// 包含此头文件可能导致定义名为“strategy”的
// 函数样式宏。
#include <string.h>

// 必须用括号包裹以避免误判
// 作为函数样式宏。
static void (strategy)();

自C11起,符合下列正则表达式的标识符不得用于具有外部链接的符号(详见第7.31节“未来库发展方向”)。类型定义和宏也有保留名称,此处不再赘述。

Pattern Header
cerfc?[fl]?cexp2[fl]?,
cexpm1[fl]?clog1[0p][fl]?,
clog2[fl]?c[lt]gamma[fl]?
complex.h
is[a-z].*to[a-z].* ctype.hwctype.h
atomic_[a-z].* stdatomic.h
str[a-z].* stdlib.hstring.h
mem[a-z].* string.h
wcs[a-z].* string.hwchar.h
cnd_[a-z].*mtx_[a-z].*,
thrd_[a-z].*tss_[a-z].*
thread.h

令人意外的是,C语言竟保留了strongislandtogether这类标识符。

补充说明:Windows头文件历来未严格规避这些保留名称。我们正努力改进新头文件,但并非所有人都收到通知。

更新:新增特殊“内部双下划线”规则。

本文文字及图片出自 On leading underscores and names reserved by the C and C++ languages

共有 161 条评论

  1. > 令人惊讶的是,C语言竟会保留诸如strong、island和together这类标识符,但事实确是如此

    好吧,我知道这只是博客文章里随口一提,但现在我不得不花大量精力去:

    1. 查明这些标识符究竟在何处被保留,因为像https://pubs.opengroup.org/onlinepubs/9699919799/functions/V…似乎并未收录?

    2. 尝试推测这些标识符的预期用途(比如island,我猜可能是作用域:仅限同一岛屿上的其他标识符访问?天啊…)

    • 这是 POSIX 规范而非 C 语言标准。https://www.iso-9899.info/n1570.html#7.31 列出了为未来使用保留的库 API 名称;该文档第 7.1.3 节明确声明这些名称已被保留。

      “island”一词本身并无特殊含义;关键在于所有以“is”开头且后接小写字母的名称均被保留(在外部链接及包含<ctype.h>的文件全局作用域内),以便未来语言版本能沿袭isalpha、isdigit等模式添加更多标准库函数, 等函数,同时避免现行标准委员会预先猜测后续版本可能采用的命名模式,且不破坏现有代码(除非现有代码本就忽略这些规则——实践中此类情况相当普遍)。

      • C语言自然是海盗最爱的语言。说不定哪天标准里真会出现“水”和“陆地”类型。届时可能需要bool iswater()bool island()函数!当你拿到岛屿结构体时,得确认它是否真是陆地,所以island(&possible_island)显然能帮上忙,也不会让代码读者困惑。

        • 哎,每个海盗的初恋都是C语言

        • 你可能需要判断是否存在被水环绕的小陆地,因此需要

            isisland(x);
          

          或者判断某个元素是否属于特定恐怖组织(或埃及女神):

            isisis(y);
          
          • 若需判断某区域是否被特定恐怖组织(或埃及女神、后金属乐队)占领,可使用经典函数:

              isisisland(z);
            

            此时通常需构建内存屏障。

        • 只需向编译器传递海盗旗即可

        • 当然,区域设置的不同会影响island()和iswater()的返回结果

      • 我看到有人指出,这些标识符限制意味着有效的C编译器优化方案是将所有“x = to[a-z]+(y);”改为“if (!is[a-z]+(y)) x = to[a-z]+(y);”。当然,现实中编译器不会蠢到实现这种优化 🙂

        这自然暗示了该优化的有效性:

        x = toilet(y); -> if (!isilet(y)) x = toilet(y);

      • 他们完全可以生成随机字符串,这样几乎不可能与现有名称冲突。而且其描述性绝不逊于C标准库中大多数现有名称。

    • 需注意原帖未反映C23标准对此议题的最新进展:标准化机构似乎注意到无人真正关注str*、is*、to*和E*这类过度宽泛的保留机制,因此引入了“潜在保留标识符”的概念——目前我尚未能理解其具体含义。相关论文《我们认为保留的符号》[1]已纳入标准草案。

      [1] https://www.open-std.org/jtc1/sc22/WG14/www/docs/n2625.pdf(希望这是正确版本)

      • 本质上,该措辞允许他们建议实现对“潜在保留标识符”的未来可能无效用法发出警告,但对实际保留标识符的无效使用仍“无需诊断”。他们认为这能让他们毫无顾忌地进一步扩展本已过多的保留标识符。

        没错,这意味着实现方会警告你代码可能在未来失效,但当真正失效时,警告就会消失。他们为何不能直接强制检测保留标识符的无效用法,我实在无法理解。

    • 在您链接的页面中,可以看到ctype.h保留了所有以is[a-z]to[a-z]开头的前缀,而string.h保留了str[a-z]。这些规则源自C标准(在C99的7.26节“未来库方向”中),不过我认为该处并未使用“保留”一词。

      • “保留”的定义出自第7.1.3节:

        每个头文件声明或定义其关联子条款中列出的所有标识符,并可选声明或定义其关联未来库方向子条款中列出的标识符,以及始终保留用于任意用途或作为文件作用域标识符的标识符。

    • 他指明了这些保留项的来源——C11标准第7.31节[1]。例如7.31.12条款:

      > 以str开头且首字母为小写的函数名可添加至<stdlib.h>头文件的声明中。

      或7.31.13条款:

      > 以str、mem或wcs开头且接小写字母的函数名可添加至<string.h>头文件声明中。

      因此根据标准规定,若代码包含string.h,使用“strong”或“memorize”这类名称可能与未来标准版本冲突。不过这种情况极其罕见。

      [1] https://port70.net/~nsz/c/c11/n1570.html#7.31

    • 这些字符串在正则表达式中被标记为保留字符,由特定头文件为未来扩展预留。“island”受ctype.h和wctype.h头文件约束,这些文件将所有以“is”开头的单词均设为保留字。

    • 这种“规则非我所立”的情形,恰好能用一句俏皮话概括。

  2. 查阅C标准后发现,POSIX进一步为自身定义了C标识符规则。这包括类型名中的_t!该规则在现实中几乎无人遵守,实在难以当真。

    • 所谓“强制”不过是未来标准版本可随意定义以_t结尾的新类型,且无需担心破坏现有代码。若你定义了专属的uint32_t类型,而新标准的uint32_t类型将其覆盖——那你(理论上)早该有所防备。

    • 尾随的_t是保留字,我多年都对此毫不知情。直到某位面试者指出这点,才让我意识到这是我题目提示中的陷阱。他们因此加分,我也学到新知识。真好奇编译器或静态分析工具为何不更频繁地报错这类问题?

      • 我好奇为什么编译器或静态分析工具不更频繁地报错?

        因为关注POSIX规范本就不在C编译器的职责范围内。

        POSIX与C标准是两回事。例如当你针对Windows API编写代码时,POSIX就无关紧要了——你更需要警惕与Windows API头文件中类型名或常量定义的冲突,而这类冲突本就无处不在。

        …况且:这条规则在现实中完全没有意义——要么你的类型名与包含头文件中的POSIX类型冲突(这种情况编译器本就会报错),要么不冲突,后者自然无碍。

        • 还有一种情况需要补充到你列出的清单中:当你需要与POSIX系统交互时,名称冲突才可能发生。

          也就是说,这些规则的主要意义在于:某些标准会规定“遵循这些规则,将使你更容易与该标准集成(如果你的计划包含此项)”。

          显然,如果你根本不打算接入POSIX,这些规则对你毫无意义。同样地,不遵循规则也不会阻碍该方向,只是可能增加额外工作量。

        • > 况且:这条规则在现实中完全毫无意义

          我认为其含义是:库作者不能将某类型命名为epoch_t,因为POSIX可能在下个版本引入epoch_t,届时某些代码可能突然无法编译。

          • 没错,但其他“标准API”同样存在这类问题,且它们的变更频率远高于POSIX。

        • > 因为关注POSIX规范并非C编译器的职责。

          当开发者编写符合POSIX规范的代码却被编译器拒绝时,这便成了编译器的责任。

          • ISO C标准本身不包含POSIX兼容性要求,因为ISO C编译器支持的平台远不止类UNIX系统。

            确保C编译器符合POSIX规范是各平台供应商的责任。

      • 对于typedef而言,_t后缀的设计如此符合人体工学,以至于我完全忽略了POSIX的这一部分规范——毕竟我的类型通常带有命名空间前缀,而POSIX本身永远不会与之发生冲突。

      • “Gotcha”这个词很好地概括了整个问题,我认为在实际代码中无需为此担忧。

    • 实际上每个C库都需要这么做,通常会预留特定前缀。

      这本质上是在声明:“若使用此API/库,需做好心理准备:未来版本可能引入符合这些模式的额外符号,且当前版本可能定义未文档化的符合这些模式的符号”。开发者可选择避免定义冲突符号。

      另一种方案是库随意添加新符号,而客户端代码无法主动预防冲突。

    • 没错,_t作为类型后缀对程序员极具吸引力。我曾提醒过人们技术上不应这么做——通常只会得到完全的漠视

  3. Windows头文件历来未严格规避这些保留名称。我们正努力改进新头文件,但并非所有人都收到通知。

    喜欢这种语气

  4. 题外话,这正是Python在我眼中显得丑陋的原因——强制使用双下划线表示等号等操作极其刺眼。

    我虽逐渐认可其作为语言的价值,但视觉上永远无法接受这种与其他语言的冲突。

    • 看SIMD代码时我也有同样感受。

      > const __m256i in = _mm256_loadu_si256((const __m256i*)ptr);

      • 这正是我宁愿为SIMD代码编写内联汇编甚至外部汇编,也不愿使用内置函数的原因。前者可读性强得多。

      • 你应该用typedef定义类型,而不是这样写。

        这就像抱怨代码里到处要用_Bool——明明可以包含stdbool头文件…

  5. 我们正努力改进新头文件规范,但并非所有人都收到通知。

    而根据mem[a-z].*模式,memo似乎是C11中禁止使用的变量名之一

  6. 我明白这是权衡取舍,但我更倾向于希望编程语言强制要求使用thisself表示成员/属性。

    C++

        foo = bar * 2;
    

    foo和bar是局部变量还是某个实例的成员?

    vs

    Python

        self.foo = self.bar * 2
    

    100%清晰。无需命名约定。

    提出此议题是因为:若语言强制要求使用selfthis,则成员采用_foo命名约定便毫无必要。

    话虽如此,我明白将独立函数重构为类方法或许更容易——毕竟无需大幅修改代码——但我很好奇这种做法究竟能带来多少净收益。

    • 从语言设计角度看,这同样表明(对C++的简单修改)

          void add(const this, int x)
      

      比(我认为)

          void add(int x) const
      

      更具可读性。

      • 不如干脆始终显式写出隐含的“this”参数。这样所有方法都会像普通函数一样呈现,既能简化语言语法和标准规范,使其更简洁一致,又不牺牲任何功能性。

      • 你可能会喜欢“推断this”提案:http://wg21.link/P0847

        (请滚动至“建议语法”部分)

      • 这可以双向实现吗?

        我赞同函数不应特殊化的观点,因此

           instance.add(10)
        

        本质上只是

           add(instance, 10)
        

        的语法糖,且第一个参数可传入任何符合条件的值。

        但遵循“语法糖无害”原则时

            class Foo {
              add(int v);
            }
        

        这不就是语法糖形式的

            void Foo.add(Foo this, int v);
        

        …或者类似的写法吗?

    • 书写符合C++惯例的代码时,最终往往还是会用到impl->foo这类写法。

      Pimpl模式某种程度上是编译器工作机制的产物,但它通常能成为结构化代码的巧妙方案。

      • 这要视具体情况而定。我今天用了PImpl,但这实属罕见例外。在可内联的小类与抽象基类之间,存在多种实现方式。但这正是C++的魅力所在:解决问题的方式永远不止一种!

    • 我在上一份工作中接触过C++,现在又和搭档在副业项目中使用。最让我抓狂的就是那些不使用this的人。

  7. 尤其在C中,遵守下划线命名规则至关重要——因为标准库大量采用纯头文件设计,意味着大量内联代码会暴露在标识符冲突风险中。例如若你将foo定义为123,而某个内联函数中有局部变量名为“foo”,就会引发问题。正因如此,在多数实现中查看标准C头文件时,所有标识符(包括局部变量)都倾向于采用_Like __this命名模式。标准库还可能需要定义自身的内部宏(因存在大量重复语法),这些宏同样遵循此命名规范。

    因此若忽视这些保留名,程序或许能编译通过,但即使实现中微小的更新(例如某个冷门标准函数新增局部变量)也可能导致崩溃,更不用说C++标准的新版本了。

  8. 我曾试图解读所有围绕保留标识符的规则,结果发现意外地棘手。

    https://gist.github.com/chjj/d0c1218e473bbb6d8f9e2224c583e2d

  9. “C语言竟会保留strong、island、together这类标识符,这或许令人惊讶,但事实确是如此。”

    是的,我猜想多数C程序员并不熟悉这些规则。是否有办法让GCC或LLVM对此发出警告?

    • clang最近新增了-Wreserved-identifier选项,我记得是在v14或v15版本。具体名称可能略有出入,但确实存在。不确定GCC是否支持

      • GCC的这个漏洞存在已有11年之久:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51437

        • 强有力的证据表明这些规则实际上无关紧要,可以忽略不计。

          • 当然!我写C语言已有40年,却不知道“total”这类无害标识符竟是保留字。

          • 我的意思是,实际上,如果Windows都做不好,编译器就算开始强制执行这些规则,至少也会提供“忽略此项”的选项。虽然Raymond Chen了解Windows并能对此发表见解,但我敢打赌Linux内核也存在大量违规情况,本质上是一样的——无论哪个C标准委员会说什么,未来的C编译器都不会让这些规则失效。

          • 更准确的说法是“自行承担风险忽略它们”

            问题不在于GCC/Clang,而在于开发者

            若你在代码中定义了strfoom,而C标准委员会后来决定将其加入string.h,你就面临名称冲突的风险

            • 问题不在开发者,而在于禁止使用[a-z].*、str[a-z].*等模式。谁会对编程语言做出这种事?

              保留这些名称是个愚蠢的决定,它通过任意禁止许多与预期功能毫无关联的常用词汇,破坏了语言的可用性。即便你不直接导出这类名称,你的库名或公司名(作为所有导出名称的前缀)也可能恰巧以这些常用组合开头,因此现在你不得不避开某些库名或公司名。

              若这种限制始于C11标准,我更感困惑——1970年代语言尚处萌芽期且资源受限尚可理解,但2011年仍如此设计完全不合逻辑。

              但愿这仅限于C11标准,绝不会出现在C++中。

              • 根本问题在于C语言缺乏命名空间,导致命名冲突不可避免且难以处理

                • C语言甚至无需命名空间就能解决这个问题,只需为标准库函数和类型添加stdc_前缀即可(参见C23标准库的stdbit.h,希望这不会是个特例)。

                  • 完善的命名空间支持导入功能,这样就不必每次都添加命名空间前缀。

                    • 没错,但我们不能直接移植C命名空间,因为这需要符号名称修饰机制。C语言可以采用“命名空间前缀”方案,例如’usingprefix stdc_’,这样既无需修改符号格式,又能实现C命名空间的大部分功能。

                  • 对于 memcpy 或 strcmp 这样基础的操作,这种写法很快就会变得非常丑陋。

                • C23 技术上必须支持属性命名空间。值得关注的是这些特性是否会被纳入核心语言。

              • 为引入新标识符,他们还应保留哪些命名空间?否则新函数将不得不采用*__std_c__strfoo()*这类冗长形式。

                • 可采取以下方案:

                  – 采用比“is”、‘to’、“str”更冷门的字母组合——这些词首在自然语言中过于常见。况且当需要添加无法用这些前缀命名的新功能时,这些前缀本身有何实际意义?

                  – 采用双下划线开头(根据文章所述该前缀已被保留)

                  -采用stdc_作为前缀

                  • 或通过链接器引入__newfunction机制,同时新增可选头文件定义短名。这正是C99中_Bool/bool(该处采用typedef)的解决方案,据我所知运行良好。

                    • 宏方案的弊端在于,当包含该头文件时,静态符号和结构体成员等也会被禁用。对于bool而言尚可接受,因其本就是可选关键词且头文件为新增内容;但若对所有函数及现有头文件都采用此方案,则可能破坏更多代码。

                  • 诸如“isalpha”、“tolower”和“strlen”这类函数诞生于C语言标准化之前,开发者只能基于现有资源进行适配。

                  • 我真心讨厌所有语言中带前缀下划线的命名方式。

                    stc_这类前缀才是正确选择。

            • 坦白说,若C标准委员会能为新加入的标准库函数添加前缀(如C23 stdbit.h中的stdc_popcount、stdc_bit_width等),会合理得多。

            • > 更准确的说法是“忽视风险自负”

              我认为编译器更新导致现有代码因“错误”变量名而失效,本质上属于编译器缺陷——即便该代码确实违反规范。如今才启用四十年前预留的关键字,规范制定已然为时过晚。

              • 事实上他们引入新名称时一直很谨慎,避免破坏现有代码。例如最终引入bool类型时,特意命名为_Bool以避免与非标准bool类型冲突。开发者需要包含<stdbool.h>才能获得友好的“bool”名称。

                _Complex的命名逻辑与此类似。

              • 按此逻辑推演,C标准库将永远无法新增任何函数(或宏、结构体等)。

                • 在合理范围内确实如此。比如若他们新增“island”函数导致我的地理软件崩溃,这显然是规范问题而非我的过错——尽管技术上我确实违反了规范。我甚至认为任何“is.*”函数在当前阶段都已错过规范修订时机。

                  • > 我甚至认为任何“is.*”函数在当前阶段都已错过规范修订时机。

                    我希望看到isualpha()、isudigit()、isuncter()、isuspace()、isugraph()等函数,以及isucombining()。它们将补充iswxxx()系列,但需满足:(a) 接受带符号Unicode整数(C2x标准定义为至少32位宽)参数,(b) 实现区域设置无关性。

      • 此外,clang-tidy 还提供了 bugprone-reserved-identifier https://clang.llvm.org/extra/clang-tidy/checks/bugprone/rese

        • 该检查仅针对以下情况:以下划线开头的保留名。

          > 此检查(目前)不涵盖其他保留名,例如与语言关键字相同的宏名,以及语言标准特有的保留名(如C++的“僵尸名”和C语言未来库方向)。

      • 它会对以下代码发出警告:

            #define _POSIX_SOURCE
        

        该定义有时需置于#include前,以便调用线程安全的现代化库函数。

    • 写了三十多年C语言,这是我第一次听说。不知自己无意中违反过多少次这类规则。

  10. 昨晚我(用C++)就因此吃过亏,完全不知道有这回事。这帖子来得真及时!

  11. 我在Dave’s Garage的YouTube节目里常听到Dave谈论Raymond,感觉这位真是传奇人物。

    • 没错,Raymond是传奇,深入挖掘他的博客存档绝对值得。

      (不过Dave的讲解风格实在令人不适,被过分高估了。)

  12. 真的,彻底避开下划线吧——它们糟透了。就算知道有排除规则的人,也搞不清具体规则。一旦用错,编译器给的错误信息根本看不懂。所以别用——谁会用这种东西?

    • 我从未见过类名开头带下划线的情况。见过尾随下划线和老式的m_前缀,但纯粹的_从未有过。

      C11新增的保留前缀让我颇感意外。现在我也在思考是否存在clang/gcc选项能对此类情况发出警告——虽然当前代码库中不存在问题(能编译运行),但我实在不愿发布公共API后又因C29之类的冲突而被迫重构。

      • 前缀下划线能区分字段_name与成员函数name,Python也有类似机制。若不采用这种方式,难道不是更混乱吗?m_真的更优吗?(诚恳提问)

        • 正如你回复的评论所提:使用尾随下划线。确实:由于前缀下划线已被保留,且保留该符号有合理依据,若你坚持拒绝使用尾随修饰符,m_ 确实优于 _。

          • 若你因某些原因坚决拒绝使用后缀修饰符

            我不会为此大做文章,但将变量作用域置于开头更符合我的逻辑。毕竟 foo.、foo-> 和 foo[] 都是放在开头的。

          • 前缀下划线仅在后续字母为大写时保留。况且C++代码中普遍存在强制字段首字母小写的编码规范。

        • 某些编码规范采用大小写区分(如成员变量’name’与获取器’Name()’)——不过我个人更倾向蛇形命名法_case。

        • 我们公司采用`m_`命名法,我逐渐体会到其价值。这种写法极大提升了代码可读性——凡是`m_`前缀的变量必属类成员字段,其余则为函数参数、局部变量或特殊前缀(如`k`表示静态常量)。

          考虑到我们阅读代码的时间远多于编写代码,在符号名称中添加这类“用法提示”能大幅减轻认知负担,效果远超预期。

          • 我认为使用m_或s_作为前缀并非坏事,但标识符存储类型的视觉提示(颜色、粗体、斜体等)应由IDE提供。

      • 自ANSI C起,某些下划线已在用户代码特定场景中被保留——根本无需使用它们。尾随下划线虽可行,但何必费心?它们既难读又难输入。

        • 尾随下划线是我解决关键字冲突的首选方案。比起随意拼写或使用关键词同义词(斜眼瞥向klazz),这种规则更具一致性且易于记忆。

          • 完全无法理解——为何要用关键词作为名称?而且评论最后部分完全不合逻辑。

  13. 所以C语言确实预先保留了某些关键词?比如island、strong、together…

    不知这种做法在某些情况下是否奏效?或者换个问法:若现在设计新编程语言,你会考虑预先保留关键词吗?

    • 我是编程语言设计师。

      我正在设计一种语言,确实会预留关键词——因为需要兼容C语言的ABI接口。但我也让这些关键词更容易规避。

      以下是我的保留词列表:

      * 以 `y_` 开头的任何词。

      * 以 `yc_` 开头的词。
      * 以 `YC_` 开头的词。
      * 包含三个及以上连续下划线的词。
      * 编辑补充:所有以下划线开头的词。这是因为我的语言必要时可转译为C语言。

      第一类用于标准库中的类型和项(语言名为Yao,故y具有特殊含义)。第二、三类分别用于C ABI(故yc)及历史兼容性。特别是YC_用于C宏。

      最后一个用于名称“修饰”。之所以加引号,是因为本语言的标准名称修饰机制(所有实现中保持一致)并非真正修饰名称,而是采用拼接方式:包名之间用五个下划线连接,包与包内元素之间用四个下划线连接,元素与其后缀之间用三个下划线连接。(对于重载函数,程序员需为每个重载定义专属后缀。该后缀即构成其在C ABI中的名称差异。)

      我希望这些规则不会过于繁琐。保留前缀的使用频率不高,且未见有人连续使用多个下划线——尽管我额外允许使用一个下划线,以防万一。

      • 遗憾的是POSIX未采用类似的保留规范,例如为命名空间添加psx_后缀。但考虑到POSIX的初衷是将成熟的*NIX规范纳入标准,以避免引发重大冲突,这种做法或许更易获得共识。

      • 我认为‘y_’规则过于严苛。编写数值计算代码时(如游戏或物理模拟),常需声明大量变量如y_1或y_ans。

        • 这规则看似严苛,但需澄清:这些规范仅适用于C语言ABI。

          在纯Yao环境中声明此类变量实际上不会冲突。访问C符号函数和类型的机制是专门设计的,其差异性正是为此目的而存在。

          • 啊,若仅限于C ABI就合理多了。不过我仍可能看到有人在编写数值算法时将 y_* 用作参数之一,尽管概率要小得多。

            • 确实如此,因此我需要进一步澄清。

              限制并非针对 `y[c]_`,而是针对 `y[c]___`。

              这是因为y是标准库的包名,而标准库名称始终通过3个或更多下划线与其余部分分隔。

              实际上,限制在于C语言名称中连续出现3个或更多下划线的情况,因此包名不能是yycYC

              对于造成的困惑我深表歉意。最初发帖时因赶时间且使用移动设备操作。

    • 新编程语言通常支持命名空间机制,可避免此类问题。

      至于收益,新版C标准通常会引入符合这些命名模式的新函数和宏,同时确保遵守保留名规则的客户端代码不受影响。

    • 若在新语言中保留某个名称,理应将其定义为错误使用。

    • 我毫不犹豫会将island、strong或together用作变量名,尽管它们技术上属于“保留”词。或许应避免使用“strong”,该词正处于被列为保留字的边缘。但应避免使用“isnull”、“strsplit”或“toint”这类变量名。不过“isNull”、“strSplit”和“toInt”仍可接受。

      我认为预先保留关键词的理念是好的,但不应轻易干扰常用词汇。其核心在于未来添加新关键词时,不会破坏当前使用该关键词的代码。

    • ES5曾有“未来保留词”列表,包括class、const、export、import和let。

      • ES5的案例颇具趣味性,因为该“未来保留字”列表中的多数词汇,实为ES4“失落版本”的保留字清单。该清单还包含至今未被采用的有趣特性,如private、public、abstract、package、byte、int、volatile、synchronized等。

        • 值得一提的是,尽管“private”早已被保留为关键字,但TC-39在几年前为类添加私有字段时,最终选择了“哈希名称”而非“private”关键字。

              class Example {
                #privateFieldName
              }
          

          而非:

              class Example {
                private privateFieldName
              }
          

          这段争论颇有意思。

      • Java将goto和const设为保留关键字。来源:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_k

      • C语言最初将entry设为保留词(源自Fortran 77,该语言允许函数拥有多个入口点)。

    • 这是预先保留特定前缀的做法。“is”用于任何返回真/假的函数(导致与“island”、“israel”、“isaac”冲突)。“str”用于任何字符串函数(导致与“strong”、‘strepthroat’、“strange”冲突)。

  14. 他未指出名称中出现双下划线的位置是任意位置,而不仅限于开头。

    此外,在实现头文件中(即使是非标准头文件),允许使用以下形式:开头下划线后接大写字母或其他保留名称。这种做法之所以被视为不良习惯,仅因其他编译器(如Clang)可能在未来读取这些头文件时,会为其赋予自定义含义。

    • 据我所知,C++中仅保留名称中任意位置的双下划线组合,而C语言仅保留以双下划线开头的名称。诚然,使用此类标识符可能不会出问题,但你几乎永远无法确保绝对安全。

      • 微软可以绝对、彻底、无可辩驳地确信,在其自身实现中使用实现保留名称是安全的。值得注意的是,某些头文件不属于实现范畴,因为它们并非随MSVS附带。但这属于包装问题,标准规范并未涵盖。

        此外,此处仅讨论C++。微软从未重视C语言,任何在C中保留的名称在C++中同样保留。

  15. 若雷蒙德·陈在微软的唯一职责就是写博客,那他的薪水绝对物有所值。他的博客始终保持着极高的水准,持续更新多年,如此高频产出令我产生冒名顶替症候群——实在难以想象他如何还能兼顾本职工作。

    • 但愿微软能多些雷蒙德·陈这样的员工,少些…那些做出“预装糖果粉碎传奇”、“再次破坏开始菜单”决策的人。Windows内核本具优雅精髓,可惜堆砌其上的高层架构实在糟糕透顶。

      • NT是个设计精妙的操作系统。真好奇微软内部还有多少人真正理解它。听说苹果现在都难觅能做内核开发的工程师。

        • 我在苹果从事视频系统开发时,因多次将他们的内核与Linux作负面比较而被绩效考核扣分。我敢肯定他们至今仍用着狗屎般的I/O调度器,不过我好久没用Mac了。

          我怀疑问题不在于工程师能力不足,而在于苹果的部门壁垒导致现有工程师既不能尝试改进,甚至不能讨论此事。

          • > 因在绩效评估中贬低其内核而受批评…工程师们甚至不被允许尝试或讨论此事

            我怀疑问题可能不在于你比较了内核,而在于你表达比较的方式。比如这段话让我觉得你可能用对抗性语气表达了观点:

            > 狗屎般的IO调度器

            或许你日常交流更得体些,我不得而知。但此处的措辞确实令人存疑。

          • 这很有意思,有没有基准测试能证明Mac调度程序的劣势?(当然不能让你违反保密协议)

            • 如今我们不再使用寻道速度慢的机械硬盘,这点差异已不那么重要了。

              对于Final Cut/iMovie这类包含大量视频/音频/杂项轨道的软件,由于缺乏调校手段,读取本应线性的数据流时,愚蠢的寻道操作很容易就让硬盘饱和。

        • 据我所知Dave Cutler仍在职,但已调离Windows团队——先去了Xbox部门,后来可能转入云计算领域?

          • 现存知名人物大多已转入Azure或开发部门(据我所知该部门现也归属Azure)。

            这很可能是所有桌面框架之间爆发GUI内战的根源。

          • “被调离”强烈暗示并非其主动选择。卡特勒在Xbox团队攻克了若干尖端的虚拟化难题,并将相关技术专长带入Azure。

        • 此类案例比比皆是,但早已一去不复返的是——当年负责Azure网络模块的工程师还能自由参与Windows开发的时代。部门壁垒本无不可,但当它们演变成象牙塔时便令人痛心。可悲的是,这种现代管理模式如今已蔓延至几乎所有企业。

        • 这本书刚出版时我就拥有了,作为资深Unix背景者,它实在精彩绝伦。

          《深入Windows NT》(微软出版社)

          https://a.co/d/1lxwnQt

      • 你似乎没理解微软的处境。

        首先,《糖果粉碎传奇》从未预装在系统中。预装的只是微软商店的购买链接,用户只需点击两次即可移除。

        其次,预装该链接使全球Windows系统的恶意软件感染率显著降低。

        微软有责任保护需要帮助的用户。找到一个折中方案——让其他用户最多只需多点两下——已经相当不错了。

        • 《糖果粉碎传奇》链接如何降低恶意软件感染率?

          顺带一提,这个链接在我笔记本上反复出现(但奇怪的是台式机没有)。

        • > 第二,预装该链接使全球Windows系统恶意软件感染率显著下降。

          为什么?因为用户会盗版游戏?还是说人们本来就会下载病毒游戏?

          • 若想建立僵尸网络,把恶意软件捆绑在低端游戏里,再免费上传可执行文件到网上,这确实是个不错的开端

        • 依据何在?为什么不捆绑《堡垒之夜》和《英雄联盟》?

      • 没有这堆东西,很多人就没事可做了,奖金理由清单上就没法写了。

    • 他总能完美平衡趣味性、基础性和精准度。感谢这些文章,每次都是下意识就点开 🙂

    • 微软每年都要搞砸他博客的所有链接,所以旧文章链接永远无法正常访问。讽刺的是,这个讨论向后兼容性的博客却总在更换网址

    • 90年代他还在Usenet论坛解答Windows编程问题。

    • 雷蒙德·陈堪称Windows开发领域的元老。

    • 还有迈克尔·卡普兰,他同样拥有极具深度的博客“Sorting it all out”,深入探讨Windows本地化、文本编码、键盘布局等议题。事实上,雷蒙德·陈曾专门推荐过他在这些领域的见解:

      https://devblogs.microsoft.com/oldnewthing/20041217-00/?p=36

      遗憾的是,微软最终并未善待他[1]。雪上加霜的是,他的博客被彻底清空。虽然存在部分存档,但尚未发现完整保留图片的版本——这往往导致博文内容难以理解,实在可惜。

      [1] https://vsubhash.wordpress.com/2017/04/17/rip-michael-j-kapl

    • 他与Russinovich——在后者加入微软之前。

    • 嗯,我想正职工作才是博客如此出色的原因吧。

    • 而且每当我有疑问时,感觉他总能给出答案。无论问题多么具体。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号