【问题标题】:Why can't variable names start with numbers?为什么变量名不能以数字开头?
【发布时间】:2010-09-25 10:09:43
【问题描述】:

不久前,我和一位新的 C++ 开发人员一起工作时,他问了一个问题:“为什么变量名不能以数字开头?”

除了某些数字中可以包含文本(123456L、123456U)之外,我无法给出答案,如果编译器认为带有一定数量字母字符的所有内容都是变量名,这将是不可能的。

这是正确的答案吗?还有其他原因吗?

string 2BeOrNot2Be = "that is the question"; // Why won't this compile?

【问题讨论】:

  • 为什么它们里面不能有空格?
  • 这个问题至少比 C++ 早了 20 年,如果不是回到第一个宏汇编器的话。
  • 好吧,在 FORTH,你可以做到。 AFAIK,有一个词叫0,它将 0 推入堆栈。另一个是0=,它检查0是否在堆栈中。
  • 为什么这个问题如此受欢迎,而答案却如此错误?许多语言确实允许变量以数字开头。 C++ 没有,但它只是一个方便的限制,可以避免某些歧义。有时 SO 会以各种错误的方式让我感到惊讶。
  • 如果今天在 SO 上提出这个问题,它将被称为基于意见并关闭。感谢您提出这个问题。

标签: c++ variables programming-languages language-design variable-names


【解决方案1】:

因为这样一串数字既是一个有效的标识符,也是一个有效的数字。

int 17 = 497;
int 42 = 6 * 9;
String 1111 = "Totally text";

【讨论】:

  • 好吧,如果他们说变量不能只是数字怎么办。然后呢?
  • 如果可能的话,我需要更长的时间才能为词法分析器提供一个正则表达式来使用该规则获取标识符,所以我明白为什么没有一种语言以这种方式实现,以及其他答案中给出的原因。
  • 如果它必须是数字+字母,那么您仍然可以使用 String 0x123 = "Hello World"。除非您声明变量名称是“不能解析为有效数字指定的数字+字母”,否则这很愚蠢。
  • 别管编译器:使用该语言的人需要能够轻松(一目了然)区分变量名和数字。如果第一个字符没有告诉您 - 相反,如果您需要搜索单词的其余部分以判断其中某处是否存在非数字字母 - 代码将更难阅读。
  • @eaolson:我使用了一个汇编器,该汇编器将该规则应用于以A-F 开头并以h 结尾的十六进制数字。当我第一次尝试定义一个标签来指向巴赫的两部分发明#13(逻辑名称?Bach)的音乐数据时,我被绊倒了。
【解决方案2】:

好好想想:

int 2d = 42;
double a = 2d;

什么是a? 2.0?还是 42?

提示,如果你不明白,数字后的 d 表示它之前的数字是双字面值

【讨论】:

  • 这实际上是一个 [相对] 较晚的符号(“d”代表“double”),C89 标准 IIRC。如果这个结构是在语言中,那么标识符中的前导数字是不可能的,但这不是数字不能开始标识符的原因。
  • d 在 C++ 中不是有效的浮动文字后缀。浮动字面量默认为双精度值,如果需要浮点数或长双精度字面值,可以使用 fl
  • 它是针对 Java 的,虽然最初的问题是针对 C++ 的,但它也适用于许多其他语言,例如 Java。但我同意。这不是标识符不能以数字开头的最初原因。
【解决方案3】:

现在这是一个惯例,但它最初是作为一项技术要求。

在过去,FORTRAN 或 BASIC 等语言的解析器不需要使用空格。因此,基本上,以下内容是相同的:

10 V1=100
20 PRINT V1

10V1=100
20PRINTV1

现在假设允许使用数字前缀。你会如何解释这个?

101V=100

作为

10 1V = 100

或作为

101 V = 100

或作为

1 01V = 100

所以,这是非法的。

【讨论】:

  • 次要问题:行号必须在第 1-6 列中,并且可执行代码在第 8 列之后。另一方面,DO 10 I=1,50 可能会被模棱两可地解析为 DO1 0I=1,50 [顺便说一句,如果有人使用句号而不是逗号,语句变成了对名为 DO10I 的浮点变量的赋值。
  • 有趣的解释!这对旧语言来说是有意义的,但我仍然想知道为什么我们仍然继续为 Python、JavaScript 或 R 等语言选择设计。
  • 我绝对记得 BASIC 的这一点,并且觉得这可能是实践中最有效的实际原因。不过从技术上讲,我隐约记得它实际上可能会回到早期的汇编语言。不过我不确定是什么汇编程序,我很可能是错的。
【解决方案4】:

因为在编译时在词法分析中避免了回溯。像这样的变量:

Apple;

当它遇到字母'A'时,编译器会立即知道它是一个标识符。

但是像这样的变量:

123apple;

编译器在命中 'a' 之前无法确定它是数字还是标识符,因此需要回溯。

【讨论】:

  • 通过记住我的编译器设计类来回答,这个答案是正确的!荣誉
【解决方案5】:

编译器/解析器/词法分析器对我来说是很久很久以前的事了,但我想我记得很难明确地确定编译单元中的数字字符是代表文字还是标识符。

由于这个原因,空间很小的语言(如 ALGOL 和原始 FORTRAN,如果我没记错的话)不能接受以数字开头的标识符。

这可以追溯到 - 在表示存储或数字基数的特殊符号之前。

【讨论】:

    【解决方案6】:

    我同意允许标识符以数字开头会很方便。一两个人提到你可以通过在你的标识符前加一个下划线来绕过这个限制,但这真的很难看。

    我认为问题的一部分来自数字文字,例如 0xdeadbeef,这使得很难为可以以数字开头的标识符制定易于记忆的规则。一种方法可能是允许任何与 [A-Za-z_]+ 匹配的不是关键字或数字文字的内容。问题是它会导致奇怪的事情,比如 0xdeadpork 被允许,而不是 0xdeadbeef。归根结底,我认为我们应该对所有肉类都公平:P。

    当我第一次学习 C 时,我记得感觉变量名的规则是任意的和限制性的。最糟糕的是,它们很难记住,所以我放弃了学习它们。我只是做了感觉正确的事情,而且效果很好。现在我学到了很多东西,它似乎并没有那么糟糕,我终于开始学习它了。

    【讨论】:

    • LOL - “问题是它会导致奇怪的事情,比如允许使用 0xdeadpork,但不允许使用 0xdeadbeef。最终,我认为我们应该对所有肉类都公平:P。”
    【解决方案7】:

    这可能是出于几个原因做出的决定,当您解析令牌时,您只需查看第一个字符以确定它是标识符还是文字,然后将其发送到正确的函数进行处理。这就是性能优化。

    另一种选择是检查它是否不是文字,并将标识符的域保留为全域减去文字。但要做到这一点,您必须检查每个标记的每个字符才能知道如何对其进行分类。

    还有文体含义标识符应该是助记符,因此单词比数字更容易记住。当许多原始语言被编写为接下来的几十年设置样式时,他们并没有考虑用“2”代替“to”。

    【讨论】:

      【解决方案8】:

      变量名不能以数字开头,因为这会导致一些问题,如下所示:

      int a = 2;
      int 2 = 5;
      int c = 2 * a; 
      

      c 的值是多少?是4,还是10!

      另一个例子:

      float 5 = 25;
      float b = 5.5;
      

      是前 5 个数字,或者是一个对象(. 运算符) 第二个5也有类似的问题。

      也许还有其他一些原因。所以,我们不应该在变量名的开头使用任何数字。

      【讨论】:

      • 即使要求标识符至少包含一个非数字字符,也必须要求包含字母的数字格式也必须包含非字母数字字符 [例如要求 0x1234 写成 $1234 和 1E6 写成 1.E6 或 1.0E6] 否则有合法和非法标识符名称的奇怪组合。
      【解决方案9】:

      限制是任意的。各种 Lisp 允许符号名称以数字开头。

      【讨论】:

        【解决方案10】:

        使用数字开头的变量名会使编译或交互过程中的错误检查更加复杂。

        允许使用以数字开头的变量名可能会给语言设计者带来巨大的问题。在源代码解析过程中,每当编译器/解释器遇到一个以数字开头的标记时,它需要一个变量名,它必须搜索大量复杂的规则来确定该标记是真正的变量还是错误.语言解析器增加的复杂性可能无法证明此功能的合理性。

        在我的记忆中(大约 40 年),我认为我从未使用过允许使用数字作为变量名开头的语言。我确信这至少完成了一次。也许,这里有人真的在哪里看到过这个。

        【讨论】:

        • 这并不难。它使词汇阶段更加困难,仅此而已。当然,当我学习编译器时,有人告诉我词法扫描可能会占用总编译时间的四分之一以上。
        【解决方案11】:

        正如一些人所注意到的,关于变量名的有效格式有很多历史包袱。语言设计者在创造新语言时总是受到他们所知道的影响。

        也就是说,几乎所有语言都不允许变量名以数字开头,因为这是语言设计的规则。通常是因为这样一个简单的规则使语言的解析和词法分析变得更加容易。不过,并非所有语言设计者都知道这是真正的原因。现代词法工具会有所帮助,因为如果您尝试将其定义为允许的,它们会给您带来解析冲突。

        OTOH,如果您的语言有一个唯一可识别的字符来预示变量名称,则可以将其设置为以数字开头。类似的规则变体也可用于在变量名中允许空格。但由此产生的语言很可能与任何流行的传统语言都不太相似,如果有的话。

        有关允许变量以数字开头并嵌入空格的相当简单的 HTML 模板语言的示例,请查看 Qompose

        【讨论】:

        • 实际上,有几种语言允许您使用字符标记标识符。它们被称为“印记”,您可以在 Perl 和 PHP 中使用它们。
        • 除非您仍然不允许在 PHP 中以数字开头的变量名 - 语言规则禁止这样做。 :-) 但是您可以在 Qompose 中使用完全相同的原因。
        【解决方案12】:

        因为如果您允许关键字和标识符以数字字符开头,词法分析器(编译器的一部分)无法轻易区分数字文字和关键字的开头,而不会变得更复杂(并且更慢) .

        【讨论】:

        • 词法分析过程很少是瓶颈。当然,它使标识符令牌的正则表达式更加复杂,但它们仍然可以是超快速的 DFA。与编译器必须完成的大多数其他任务相比,它们的运行时间很短。
        【解决方案13】:

        COBOL 允许变量以数字开头。

        【讨论】:

          【解决方案14】:

          C++ 不能拥有它,因为语言设计者将它定为规则。如果您要创建自己的语言,您当然可以允许它,但您可能会遇到与他们相同的问题并决定不允许它。会导致问题的变量名示例:

          0x, 2d, 5555

          【讨论】:

          • 此限制适用于不允许这种语法的语言。
          【解决方案15】:

          放松句法约定的关键问题之一是它在编码过程中引入了认知失调。您对代码的看法可能会受到这将引入的缺乏清晰度的深刻影响。

          Dykstra 不是说过“任何工具最重要的方面是它对用户的影响”吗?

          【讨论】:

            【解决方案16】:

            编译器有7个阶段如下:

            1. 词法分析
            2. 语法分析
            3. 语义分析
            4. 中间代码生成
            5. 代码优化
            6. 代码生成
            7. 符号表

            在编译这段代码时,在词法分析阶段避免了回溯。像Apple这样的变量,编译器在词法分析阶段遇到字母“A”字符时会立即知道它的标识符。然而,像 123apple 这样的变量,编译器将无法确定它是数字还是标识符,直到它命中“a”,它需要回溯才能进入词法分析阶段以识别它是一个变量。但编译器不支持。

            当您解析令牌时,您只需查看第一个字符以确定它是标识符还是文字,然后将其发送到正确的函数进行处理。这就是性能优化。

            【讨论】:

              【解决方案17】:

              可能是因为它使人们更容易分辨它是数字还是标识符,并且因为传统。具有可以以数字开头的标识符不会使词法扫描变得那么复杂。

              并非所有语言都禁止使用以数字开头的标识符。在 Forth 中,它们可以是数字,小整数通常被定义为 Forth 字(本质上是标识符),因为读取“2”作为例程将 2 压入堆栈比将“2”识别为数字更快其值为 2。(在处理来自程序员或磁盘块的输入时,Forth 系统会根据空格分割输入。它会尝试在字典中查找标记以查看它是否是已定义的单词,并且if not 会尝试将其转换为数字,如果 not 则会标记错误。)

              【讨论】:

              • 问题是 Forth 并没有一个非常复杂的解析器。实际上,它所关心的只是标识符是否位于两组空白之间。
              【解决方案18】:

              假设您确实允许符号名称以数字开头。现在假设您想命名一个变量 12345foobar。您如何将其与 12345 区分开来?使用正则表达式实际上并不难。问题实际上是性能之一。我无法真正解释为什么这是非常详细的,但它基本上归结为将 12345foobar 与 12345 区分开来需要回溯的事实。这使得正则表达式具有不确定性。

              这个here有更好的解释。

              【讨论】:

              • 如何设计一个正则表达式来允许名为ifqdoublez 但不允许ifdouble 的变量?允许标识符以数字开头的根本问题是存在完全由字母数字字符组成的十六进制文字和浮点数的现有形式(语言将使用 $1234 或 h'1234 之类的东西而不是 0x1234,并且需要像1E23 包括一个句点,可以避免这个问题)。请注意,尝试正则表达式解析 C 可能已经被 0x12E+5 之类的东西绊倒了。
              【解决方案19】:

              编译器很容易在内存位置上使用 ASCII 而不是数字来识别变量。

              【讨论】:

                【解决方案20】:

                我认为简单的答案是可以,限制是基于语言的。在 C++ 和许多其他语言中它不能,因为该语言不支持它。它没有内置到允许这样做的规则中。

                这个问题类似于在国际象棋中问为什么国王不能一次移动四个空格?这是因为在国际象棋中这是非法的举动。可以确定在另一场比赛中。这仅取决于正在播放的规则。

                【讨论】:

                • 除了 C++ 是最近由还活着的人发明的。我们可以问他们为什么选择他们所做的事情,并拒绝替代方案。同样不适用于国际象棋。
                • 但这不是我要说的重点。类比为什么不能在变量名的开头有数字,最简单的答案是,因为语言规则不允许。
                • 当然,但我不认为提问者是白痴。他可能已经独自完成了这么远。 IMO 的问题是“为什么语言规则不允许这样做?”。他想在了解规则和理解规则之间架起一座桥梁。
                • 是的,经过反思,我意识到你要去哪里。你有一定道理。我想我有点随意地应用奥卡姆剃刀,并假设除了变量不以数字开头之外没有真正的答案,因为没有数字。
                • 我并不是说你错了,请注意,C++ 标准机构的决定有时确实超出了凡人的理解,你最终会得到“因为他们必须决定某事,他们决定了这个” .但至少有一个问题要问:-)
                【解决方案21】:

                最初只是因为它更容易记住(您可以赋予它更多含义)变量名称作为字符串而不是数字,尽管数字可以包含在字符串中以增强字符串的含义或允许使用相同的变量名,但将其指定为具有单独但密切的含义或上下文。例如 loop1、loop2 等总是让你知道你在一个循环中和/或循环 2 是 loop1 中的一个循环。 您更喜欢哪个(更有意义)作为变量:地址还是 1121298?哪个更容易记住? 但是,如果语言使用某些东西来表示它不仅仅是文本或数字(例如 $address 中的 $),它真的不应该有所作为,因为这会告诉编译器接下来的内容将被视为变量(在这种情况下)。 无论如何,这取决于语言设计者想要使用什么作为他们的语言规则。

                【讨论】:

                  【解决方案22】:

                  编译器在编译期间也可以将变量视为一个值 所以值可能会一次又一次地递归调用该值

                  【讨论】:

                    【解决方案23】:

                    在编译这段代码时避免了词法分析阶段的回溯。苹果之类的变量; ,当在词法分析阶段遇到字母‘A’字符时,编译器会立即知道它的标识符。但是,像 123apple 这样的变量; ,编译器将无法确定它是数字还是标识符,直到它命中“a”并且它需要回溯才能进入词法分析阶段以识别它是一个变量。但编译器不支持。

                    Reference

                    【讨论】:

                      【解决方案24】:

                      在声明变量时可能没有任何问题。但是当它尝试在其他地方使用该变量时会出现一些歧义:

                      let 1 = "你好,世界!" 打印(1) 打印(1)

                      print 是一个接受所有类型变量的通用方法。所以在那种情况下编译器不知道程序员指的是哪个(1):整数值的1或存储字符串值的1。 在这种情况下,编译器可能更好地允许定义类似的东西,但是当尝试使用这种模棱两可的东西时,会带来一个具有纠正能力的错误,以说明如何修复该错误并清除这种歧义。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-10-28
                        • 2014-03-09
                        • 2013-06-30
                        • 1970-01-01
                        • 2022-11-16
                        • 2010-10-07
                        • 1970-01-01
                        相关资源
                        最近更新 更多