【问题标题】:What obscure syntax ruined your day? [closed]什么晦涩的语法毁了你的一天? [关闭]
【发布时间】:2012-05-01 20:50:40
【问题描述】:

您何时遇到过可能过时、从未使用过或只是简单地混淆了您一生都无法理解的语法。

例如,我从来不知道逗号是 C 中的实际运算符。所以当我看到代码时

if(Foo(), Bar())

我刚刚吹了一个垫圈,试图弄清楚那里发生了什么。

我很好奇其他语言中可能存在哪些从未被尘封过的小角落。

【问题讨论】:

  • 所以我很好奇,逗号有什么作用?还是超载了?
  • 除非逗号被重载,否则它会从左到右执行参数并返回最右边的值。基本上就像使用;,但可以包含在 if、for 等中。它有时用于 for 循环:for(int a = 0, b = 100; a
  • 我同意@Tomblin。这应该是一个社区 wiki 问题。没有正确的答案(除其他外)。
  • 是C,所以不能重载。还有你的右边的strager(除了它从左到右评估,然后执行略有不同)。我会把它做成一个wiki。
  • 不是左右实现依赖吗?或者那只是 C++?

标签: syntax


【解决方案1】:

局部变量的默认构造函数的 C++ 语法。起初我写了以下内容。

Student student();  // error
Student student("foo");  // compiles

这使我花了大约一个小时阅读一个神秘的 C++ 错误消息。最终,一个非 C++ 新手路过,笑着指出了我的错误。

Student student;

【讨论】:

  • 啊!我讨厌这个!我经常遇到的 C++ 中的一个不一致之处。
  • 这是纯粹的邪恶,很容易忘记
  • 其实 C++ 在这方面是非常一致的——规则是“如果它可以以任何方式被视为一个声明,那么它就是一个声明”
  • @strager - 这与 C# 不一致!呵呵。 8-)
  • 第一个不是错误,它声明了一个没有参数并返回一个学生对象的函数。该语法可以追溯到 C。
【解决方案2】:

这总是不和谐的:

std::vector <std::vector <int> >
                              ^
                              mandatory space.

【讨论】:

  • 谢天谢地,现在大多数编译器都修复了这个问题。这让我发疯了
  • 哦,很高兴知道 - 我还在做。
  • 终于在c++0x中变得合法了。
【解决方案3】:

当使用 System.DirectoryServices 名称空间绑定到 ADAM(Active Directory 应用程序模式;我认为现在称为 AD LDS)时,我花了一整天的时间来调试这个简单的代码:


DirectoryEntry rootDSE = new DirectoryEntry(
    "ldap://192.168.10.78:50000/RootDSE",
    login, 
    password, 
    AuthenticationTypes.None);

当我运行代码时,我不断收到带有错误 0x80005000 的 COMException,这有助于映射到“未知错误”。

我可以使用登录名和密码并通过 ADSI Edit 绑定到端口。但是这行简单的代码行不通。奇怪的防火墙权限?配置有问题?某些 COM 对象未正确注册? 为什么它不工作?

答案?它是 LDAP://,而不是 ldap://。

这就是我们喝酒的原因。

【讨论】:

  • 啊回忆。不再在 X500 土地上,但你的代码把我送回来了。
  • Active Directory 和 LDAP - 为您的桌面带来大型机的复杂性!
【解决方案4】:

C++

class Foo
{
    // Lots of stuff here.
} bar;

bar 的声明很难看。在 C 中更常见,但在 C++ 中尤其令人讨厌。

【讨论】:

  • cfront 2.0 用于发出警告
  • 我在代码审查中也是如此。
【解决方案5】:

Perl 的语法不久前让我很糟糕:

%table = {
  foo => 1,
  bar => 2
};

如果没有适当的警告(在我使用的平台上不可用),这将创建一个单元素哈希,其中键作为给定的哈希引用和值 undef。请注意 {} 的微妙用法,它创建了一个新的哈希引用,而不是 (),它是一个用于填充 %table 哈希的数组。

【讨论】:

  • 我认为你可以用 Perl 为这个问题提供数百个答案。
  • Perl 的语法总是给我带来糟糕的一天。
  • 到目前为止,Perl、Python、C# 和 Powershell 各有其一,而 C 和 C++ 是唯一有多个语法陷阱的。看看有多少 Perl 确实 最终会很有趣:DWIMminess 意味着永远不必说“wth”:-)
【解决方案6】:

第一次看到 Python 的准三元运算符不是语法错误,我惊呆了:

X if Y else Z

【讨论】:

  • 我见过 X 和 Y 或 Z。更令人困惑,因为它不是真正的三元运算符...
  • 是的,但是 python 的非典型 and and or 用法有很好的记录。这不是。
  • Python 的主要目的是让代码更具可读性,虽然这个结构起初令人惊讶,但它更具可读性。
  • 我仍然觉得它令人困惑,因为它将 if-then-else 更改为 then-if-else。至少 C 风格的 ?: 运算符保留了该顺序。
  • 我认为这是为了与x for x in whatever等保持一致
【解决方案7】:

这是愚蠢而常见的,但这种语法:

if ( x = y ) {
    // do something
}

在过去的一年里,用几种不同的语言吸引了我大约 3 次。我真的很喜欢 R 语言使用

x <- y

如果 x = y 语法表示 x == y,x

【讨论】:

  • 现代编译器对 = 是赋值表达式且 == 是比较运算符的情况发出警告。不过,您是对的,不幸的是 = 通常用于表示破坏性分配。帕斯卡有一个更好的主意:=。
  • 你是在讽刺吗?
  • 这是 Icon 使用 := 进行分配的原因之一。另一种是=可以用于数值比较(==是字符串比较)。
  • 另一种常见的解决方案是遵循编码标准,将不可赋值的表达式放在左侧,这样(例如)“if (1 = x)”甚至无法编译。
【解决方案8】:

C/C++ 的位向量语法。最糟糕的部分是尝试仅根据语法搜索它。

struct C {
  unsigned int v1 : 12;
  unsigned int v2 : 1;
};

【讨论】:

  • 让我们包括一个事实,即标准规定您必须在此处明确指定有符号或无符号。
  • @Joshua, strager SO 文本编辑器编译器很弱。
  • 我最近被一个位域中的内部顺序没有保证的事实欺骗了......
  • Jared - Google 搜索“位域”。
  • @notwithstanding 但是当你第一次看到这个时你怎么知道它是一个位域?
【解决方案9】:

C# 的 ??操作员第一次看到它时就把我扔了一个循环。本质上,如果它不为空,它将返回 LHS,如果 LHS 为空,它将返回 RHS。

object bar = null;
object foo = bar ?? new Student();  // gets new Student()

【讨论】:

  • @strager,是的。这就是你得到的取决于 SO 的编辑器进行编译
  • 如果使用完全不明显的语法,那实际上非常漂亮:-)。
  • 这就像 SQL 中的 COALESCE,如果你知道就好了!
  • 如果你把它们堆叠起来真的很酷:object foo = parameter["foo"] ??本地默认 ??全局默认;
  • 我不知道它存在,这真的很方便:)
【解决方案10】:

Powershell 的函数调用语义

function foo() { 
  params ($count, $name);
  ...
}

foo (5, "name")

对于那里的非 powershellers。这会起作用,但不会像您期望的那样起作用。它实际上创建了一个数组并将其作为第一个参数传递。第二个参数没有明确的价值。正确的版本是

foo 5 "name"

【讨论】:

  • 我发现 Powershell 几乎无法使用,因为这种调用函数的语法。
  • 此语法是标准的 shell 语法,已被 Haskell、ML 风格的语言和 Lisp 在较小程度上采用。我发现打字更好——更少的语法妨碍你。在开始编写脚本之前了解 shell 约定;)。
【解决方案11】:

我第一次看到 C++ 中的函数指针时,我很困惑。更糟糕的是,因为语法没有关键词,所以很难查。为此,在搜索引擎中输入的具体内容是什么?

int (*Foo)(float, char, char);

我最终不得不问当地的 C++ 大师它是什么。

【讨论】:

  • 不是“typedef int (*Foo)(float, char, char);”
  • @FryGuy:不知道你的意思,但是不,不需要为类型引入别名,这就是 typedef 所做的。
  • 是的。通常它是作为“typedef int (*foo_t)(float, char, char);”完成的,然后是“foo_t bar;”,至少,根据我的经验。
  • @Steve 你问cdecl :)
【解决方案12】:

VB 的(是的,我必须使用它)“And”关键字 - 如:

If Object IsNot Nothing And Object.Property  Then

在我确定对象不为 NULL 之后,看到那个 Object.Property 引用了吗?好吧,VB 的“And”关键字 * 不会 * 不会 * 阻止 * 进一步 * 评估,所以代码会失败。

然而,VB 确实有另一个关键字 - AndAlso:

If Object IsNot Nothing AndAlso Object.Property Then

这将按您的预期工作,并且在运行时不会爆炸。

【讨论】:

  • 太可怕了......他们怎么会认为“和”不应该短路???
  • OPTION STRICT 将对该选项发出警告。 Ed, And 是按位的。
  • 他们是如何以及如何到达AndAlso的?
【解决方案13】:

我曾经对一些声明对局部变量的引用但从未使用它的 C++ 代码感到非常困惑。类似的东西

MyLock &foo;

(在语法上稍微松懈一下,我已经快 8 年没学过 C++了)

去掉那个看似未使用的变量会使程序开始以与这个“未使用”变量无关的晦涩方式死亡。所以我做了一些挖掘,发现该类的默认 ctor 抓住了一个线程锁,并且 dtor 释放了它。这个变量在看似没有做任何事情的情况下保护代码免受同时更新。

【讨论】:

  • 你写的那行代码是非法的(AFAIK)。你的意思是他依赖 RAII,只写“MyLock foo”; ?
  • 那是 RAII,一个很常见的习语,但我猜从未见过它的程序员可能会认为我们(C++ 程序员)疯了。并不是说我们不是;)
  • 讨厌的“错误”,但我喜欢这种锁定方式。一个很好的例子,说明了良好的变量和类名的重要性。
  • 就个人而言,我更喜欢这样的东西:MyLock fooLock(&foo);我同意这是一个很棒的机制,但有时并不明显(就像在这种情况下)。
  • 抱歉吹毛求疵,但这并不是语法问题...
【解决方案14】:

Javascript:这个语法...

for(i in someArray)

... 用于遍历数组,或者我是这么认为的。一切正常,直到另一个团队成员加入 MooTools,然后我的所有循环都被破坏了,因为 for(i in ...) 语法还覆盖了已添加到数组对象的额外方法。

【讨论】:

    【解决方案15】:

    不得不将一些科学代码从旧的 FORTRAN 翻译成 C。有几件事毁了我的一天:

    打孔卡缩进。每行的前 6 个字符保留用于控制字符、转到标签、cmets 等:

    ^^^^^^[code starts here]
    c     [commented line]
    

    循环的 Goto 样式编号(加上 6 个空格缩进):

              do 20, i=0,10
              do 10, j=0,10
                 do_stuff(i,j)
    
        10         continue
        20         continue
    

    现在想象有多个嵌套循环(即do 20do 30),它们没有区分缩进来知道您所处的上下文。哦,终止语句距离数百行。

    格式声明,再次使用 goto 标签。代码写入文件(用数字 1,2 等有用地引用)。要将 a,b,c 的值写入我们的文件:

    write (1,51) a,b,c
    

    所以这会在标有标签 51 的行使用格式语句将 a、b、c 写入文件 1:

    51      format (f10.3,f10.3,f10.3)
    

    这些format 行与调用它们的位置相距数百行。这因作者决定使用以下方式打印换行符而变得复杂:

    write (1,51) [nothing here]
    

    小组中的一位讲师可靠地告诉我,我很轻松。

    【讨论】:

      【解决方案16】:

      C 的逗号运算符对我来说似乎不是很模糊:我一直都看到它,如果没有,我可以在 K&R 的索引中查找“逗号”。

      现在,三元组是另一回事...

      void main() { printf("wat??!\n"); }  // doesn't print "wat??!"
      

      维基百科有一些很好的例子,来自真正令人困惑的:

      // Will the next line be executed????????????????/
      a++;
      

      到奇怪的有效:

      /??/
      * A comment *??/
      /
      

      甚至不要让我开始研究有向图。如果这里有人可以从记忆中完全解释 C 的有向图,我会感到惊讶。快速,C 有哪些有向图,它们在解析时与三合图有何不同?

      【讨论】:

      • 是的,我有一次不小心学到了三元组。
      【解决方案17】:

      在启用 /clr 的 C++ 中类似这样的语法。尝试在 C++ 中创建托管字典对象。

      gcroot^> m_myObjs;

      【讨论】:

        【解决方案18】:

        老歌:

        在PL/1中没有保留字,所以你可以定义与语言关键字同名的变量、方法等。

        这可以是有效的代码行:

        IF ELSE THEN IF ELSE THEN
        

        (ELSE 是布尔值,IF 和 THEN 显然是函数。)

        【讨论】:

          【解决方案19】:

          Iif(condition, expression, expression) 是函数调用,而不是运算符。

          条件的两边总是被评估。

          【讨论】:

          • 您能否在第二条评论中详细说明?使用 || 时,如果左边的一个为零,则只计算一个。
          • VB.NET 的 Iif 不能这样工作。
          【解决方案20】:

          如果我必须读/写很多 HP 计算器中使用的某种Polish notation,那总是会毁了我的一天......

          【讨论】:

            【解决方案21】:

            PHP 的三元运算符从左到右关联。有一天我在学习 PHP 时,这让我非常痛苦。在过去的 10 年中,我一直在使用 C/C++ 编程,其中三元运算符从右到左关联。

            我仍然有点好奇为什么 PHP 的设计者选择这样做,而在许多其他方面,PHP 的语法与 C/C++ 相当接近。

            编辑:现在我只在胁迫下使用 PHP。

            【讨论】:

            • 这是 1 个??我停止使用 PHP 的原因(并认为它是一种劣等语言)。 +1
            • 我总是将三元运算符完全括起来。
            【解决方案22】:

            不是很晦涩,但每当我用一种语言编写太多代码,然后又回到另一种语言时,我就会开始弄乱后者的语法。当我意识到 C 中的“#if”不是注释(而是更致命的东西)并且 Python 中的行不需要以分号结尾时,我总是自嘲。

            【讨论】:

              【解决方案23】:

              在对一些 C++ 代码执行维护时,我曾经发现有人做过这样的事情:

              for (i=0; i<10; i++)
              {
                 MyNumber += 1;
              }
              

              是的,他们有一个循环将 1 加到一个数字上 10 次。

              为什么它毁了我的一天?肇事者早就离开了,我不得不修复他们的模块。我以为如果他们这样做,天知道我还会遇到什么!

              【讨论】:

              • 如果为 MyNumber 的类类型重载了 += 运算符怎么办?可能不会按照你的想法去做。无论如何,我相信你已经检查过了,无论如何它真的很阴险。
              • 我真正讨厌该代码的是使用“我的”命名空间。我真的很讨厌“我的”命名空间。你为什么拥有它?不是我们的吗?你怎么这么自私?
              • @jcoffland。不,开发人员在重载 int 类型的运算符时一点也不聪明(这在 C++ 中甚至可能吗?)
              • 当然可以。 int operator+=(int x, int y) {return x -= y;} // 邪恶!
              【解决方案24】:

              AT&T 汇编语法 >:(

              这种反直觉、晦涩难懂的语法毁了我很多天,例如简单的 Intel 语法汇编指令:

              mov dword es:[ebp-5], 1 /* Cool, put the value 1 into the 
                                       * location of ebp minus five.
                                       * this is so obvious and readable, and hard to mistake
                                       * for anything else */
              

              翻译成 AT&T 语法

              movl $1, %es:-4(%ebp) /* huh? what's "l"? 4 bytes? 8 bytes? arch specific??
                                     * wait, why are we moving 1 into -4 times ebp?
                                     * or is this moving -4 * ebp into memory at address 0x01?
                                     * oh wait, YES, I magically know that this is
                                     * really setting 4 bytes at ebp-5 to 1!
              

              更多...

              mov dword [foo + eax*4], 123 /* Intel */
              mov $123, foo(, %eax, 4)     /* AT&T, looks like a function call...
                                            * there's no way in hell I'd know what this does
                                            * without reading a full manual on this syntax */
              

              还有我的favorites之一。

              就好像他们采用了操作码编码方案并尝试将其合并到编程语法中(阅读:scale/index/base),但也尝试在数据类型上添加一层抽象,并将该抽象合并到操作码名称会引起更多混乱。我看不出有人能认真地用这个编程。

              【讨论】:

              • 你不应该使用它。 AT&T 汇编语法旨在由编译器输出。
              • 我希望如此。但不会改变人们在其中编程的事实。
              【解决方案25】:

              在用于诉讼文件审查的独立数据库软件 (Concordance) 的脚本语言 (Concordance Programming Language) 中,数组索引为 0,而(某些)字符串函数索引为 1。从那以后我再也没碰过它。

              【讨论】:

                【解决方案26】:

                This。我不止一次遇到过它。

                【讨论】:

                  【解决方案27】:

                  GNU 扩展通常很有趣:

                  my_label:
                    unsigned char *ptr = (unsigned char *)&&my_label;
                    *ptr = 5; // Will it segfault?  Finding out is half the fun...
                  

                  成员指针的语法也让我很伤心,更多是因为我不经常使用它,而不是因为它有什么非常棘手的地方:

                  template<typename T, int T::* P>
                  function(T& t)
                  {
                    t.*P = 5;
                  }
                  

                  但是,真的,谁需要讨论 C++ 中晦涩的语法?通过运算符重载,您可以发明自己的!

                  【讨论】:

                  • 该操作符对于加速VM中的字节码解释非常有帮助。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-07
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多