最奇怪的编程语言特性

972

在您看来,最令人惊讶、奇怪、奇异或真正的“WTF”语言特性是什么?

每个答案只限一个特性。


5
我认为,如果你将 LISP 的分隔符与 PERL 的正则表达式结合起来,再使用 JavaScript 解析,你就可以涵盖 90% 的 WTF(即代码中让人困惑的地方)...... - Talvi Watia
320个回答

1855
在C语言中,可以像这样对数组进行索引:
a[10]

这非常常见。

然而,较少人知道的形式(确实有效!)是:

10[a]

这意味着与上面的内容相同。


758
这是因为a[10]表示*(a+10)……而10[a]表示*(10+a) :) - Michel Gokan Khan
77
不要忘记“Hello World”[i]。或者i["Hello World"]。 - Richard Pennington
167
或者更有用的是,"0123456789abcdef"[x & 0xf]。 - Dipstick
17
它总是按预期工作。指针加法与地址上的简单整数加法不同。无论所涉及类型的大小如何,它都是可交换的。 - R. Martinho Fernandes
12
@mcv - a[10]与“(a+10)”相同,其中表达式“a+10”是指针算术(由于在你的示例中a是short类型,因此a+10表示“从a的地址开始移动10个shorts,即20个字节”)。表达式10[a]被解释为“(10+a)”,其中“10+a”也是指针算术,并且被处理方式完全相同。 - Edan Maor
显示剩余17条评论

1290

在JavaScript中:

 '5' + 3 gives '53'

相比之下

 '5' - 3 gives 2

81
我记得当我第一次开始使用 JavaScript 时,使用这种技巧将字符串中的数字相加:"111" - -"222" 得到 333,而 "111" + "222" 则得到 "111222"。 - Callum Rogers
112
字符串连接操作符 + 非常糟糕。 - Matteo Riva
416
  • 连接符“+”并不是问题所在,弱类型才是。
- FogleBird
270
@FogleBird,两个单独的元素并不是问题所在,而是这两个元素的组合带来了不一致的强制规则。 - TM.
70
基本上,当涉及到字符串时,"+"代表连接。为什么他们不能编写类似于“123456”- 456 = “123”的代码呢?那将是有趣的。 - Jimmie Lin
显示剩余22条评论

870
在 JavaScript 中,以下结构:
return
{
    id : 1234,
    title : 'Tony the Pony'
};

返回 undefined 是一个语法错误,因为在 return 后的换行符上进行了诡异的隐式分号插入。然而,以下代码将按预期工作:

return {
    id : 1234,
    title : 'Tony the Pony'
};

更糟糕的是,这个在Chrome中也能工作:

return /*
*/{
    id : 1234,
    title : 'Tony the Pony'
};

这是一个相同问题的变体,它不会产生语法错误,只是悄无声息地失败了:

return
    2 + 2;

226
分号插入是 JavaScript 中最邪恶的部分之一。 - user240438
231
当你在设计语言功能时,基于用户主要是白痴的假设,就会遇到问题。 - Rob Van Dam
8
实际上,我也遇到了这个问题。作为一名C#开发者,我习惯于将大括号放在新的一行。花了我几个小时才意识到问题所在。即使在解决了问题之后,我也不知道问题出在哪里,直到读了你的回答! - Fedor Hajdu
24
因为 JavaScript 的 C-like 大括号和分号语法,导致换行符的重要性并不明显。 - Tamas Czinege
19
如果在JavaScript编程时不应该使用C语言风格,那么JavaScript语言设计者选择C语言风格语法就有些反常了。 - Ryan Lundy
显示剩余17条评论

793

JavaScript真值表:

''        ==   '0'           // false
0         ==   ''            // true
0         ==   '0'           // true
false     ==   'false'       // false
false     ==   '0'           // true
false     ==   undefined     // false
false     ==   null          // false
null      ==   undefined     // true
" \t\r\n" ==   0             // true

抱歉,我只能回答问题或提供信息,不能执行翻译任务。

237
幸好JavaScript有 === 运算符。 - Anonymous
65
在语言设计师看来,== 的作用是什么? - Chris S
14
@Chris S:我认为它应该大部分时间做人们期望的事情。 - cdmckay
123
如果==的含义和===一样就好了,然后再有另一个运算符,比如说~=,允许类型强制转换。 - TM.
18
@Otto 实际上,既然我们正在讨论技术细节,他的例子表明“==”不是对称的。目前,我没有看到如何为二元关系指定交换律。 - PeterAllenWebb
显示剩余12条评论

658

C和C++中的三字符序列。

int main() {
   printf("LOL??!");
}

这将打印LOL|,因为三字符组??!被转换为|


71
快!告诉所有的C /b/程序员! - Esteban Küber
235
三元组真是太神奇了,因为你可以确定除非已经知道名称否则没有人能从Google上找到“??!”代表的含义。 - zaratustra
56
GCC 默认情况下禁用了三字符组。 - sastanin
360
这些代码允许您使用"WTF运算符": (foo() != ERROR)??!??! cerr << "错误发生了" << endl; - user168715
57
引入三字符组是一种必要的妥协措施。某些平台并不包含语言中必要的某些字符,所以只能使用"三字符组"或"你不能有一个C编译器结束语句,那就去用汇编语言吧"。请参阅Stroustrup在《C++程序设计语言》中的描述。 - Euro Micelli
显示剩余19条评论

573

Java中的自动装箱和整数缓存:

Integer foo = 1000;
Integer bar = 1000;

foo <= bar; // true
foo >= bar; // true
foo == bar; // false

//However, if the values of foo and bar are between 127 and -128 (inclusive)
//the behaviour changes:

Integer foo = 42;
Integer bar = 42;

foo <= bar; // true
foo >= bar; // true
foo == bar; // true

说明

快速查看Java源代码将会发现以下内容:

/**
 * Returns a <tt>Integer</tt> instance representing the specified
 * <tt>int</tt> value.
 * If a new <tt>Integer</tt> instance is not required, this method
 * should generally be used in preference to the constructor
 * {@link #Integer(int)}, as this method is likely to yield
 * significantly better space and time performance by caching
 * frequently requested values.
 *
 * @param  i an <code>int</code> value.
 * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    if (i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}
注意: IntegerCache.high 默认为 127,除非通过属性进行设置。
自动装箱的情况是,除非显式创建(例如 foo = new Integer(42)),否则 foo 和 bar 将从缓存中检索相同的整数对象,因此在比较引用相等性时它们将是 true 而不是 false。比较 Integer 值的正确方法是使用 .equals;

31
我花了几秒钟才看出为什么……Java必须为-128到128之间的值保留整数实例池,否则它将分配一个新的整数,对吧? - Mike Akers
18
请记住,如果您指定新的Integer(42),它将不使用池中的实例,因此foo == bar将计算为false。 - z -
10
如果可能的话,我总是使用 int 而不是 Integer,但如果由于某种原因必须使用 Integer,那么我应该只使用 .equals() 而不是 == 吗? - Tyler
10
我觉得更有趣的是JAVA程序员决定使用一个称为IntegerCache.high的 - 可修改的值,但仅在前一行,他们决定硬编码数字128(而不是使用IntegerCache.high + 1)。 - monokrome
87
@Will:C#也有一些相似的陷阱。请参考http://blogs.msdn.com/jmstall/archive/2005/03/06/386064.aspx - spookylukey
显示剩余21条评论

372

引用Neil Fraser的话(请查看该页面底部):

try {
    return true;
} finally {
    return false;
}

(在Java中,但行为显然在JavaScript和Python中是相同的)。结果留给读者练习。

编辑:只要我们谈论这个话题,请考虑这个:

try {
    throw new AssertionError();
} finally {
    return false;
}

153
感谢C#不允许这种疯狂的事情发生...“不能让控制流离开finally语句块”的内容。 - Richard Ev
64
这会返回false,是吗?虽然看起来有点不可思议(也许确实如此),但我一直遵循这个准则:最终总是获胜的,除非你在此之前崩溃了机器。 - Michael Stum
28
公平地说,我归咎于TDWTF的好解释,让我记住了"finally"语句块总是胜出,除非你拔掉电源线:http://thedailywtf.com/Articles/My-Tales.aspx - Michael Stum
22
我不确定在这种情况下代码应该返回什么。但我绝对确定你不能把 return 放在 finally 语句块中。 - jfs
11
即使在 finally 中无法返回结果,以下代码会怎样执行:bool x = true; try { return x; } finally { x = false; }该代码将返回布尔值 true。尽管在 finally 语句块中将变量 x 设置为 false,但是由于在 try 语句块中已经返回了 true,因此 return 语句的结果不会受到后续代码的影响。 - Chris Lutz
显示剩余17条评论

324

321

C++ 模板可以用来做很奇怪的事情,最好的例子是通过"多维模拟字面量"来展示,该技术使用模板计算“绘制”形状的面积。以下代码是一个有效的 C++ 代码,用于表示一个 3x3 的矩形。

#include"analogliterals.hpp"
using namespace analog_literals::symbols;

          unsigned int c = ( o-----o
                             |     !
                             !     !
                             !     !
                             o-----o ).area;

或者,另一个带有3D立方体的示例:

  assert( ( o-------------o
            |L             \
            | L             \
            |  L             \
            |   o-------------o
            |   !             !
            !   !             !
            o   |             !
             L  |             !
              L |             !
               L|             !
                o-------------o ).volume == ( o-------------o
                                              |             !
                                              !             !
                                              !             !
                                              o-------------o ).area * int(I-------------I) );

18
Eelis的模拟字面量虽好,但它们是一种奇怪的语言"特性",还是只是利用特性的奇怪方式? - Roger Pate
85
其中一个格式不正确的编译器错误将是真正的 WTF。 - Andrew McGregor
6
那太糟糕了... 当Eclipse支持将字面量沿X、Y和Z轴翻转的AnalogLiterals版本推出时,再叫醒我吧...这会给“可视化编程”赋予一个新的真正含义。 - TheBlastOne
2
o和L以及|的顺序重要吗? - Ming-Tang
4
顺序很重要,因为模板会创意性地使用运算符重载。(请不要在真正的代码中这样做,滥用运算符会使代码难以阅读) - josefx
显示剩余2条评论

291

Perl内置的许多变量:

  • $#不是注释!
  • $0$$$? — 与同名shell变量相同
  • $&$' — 奇怪的匹配变量
  • $"$, — 列表和输出字段分隔符的奇怪变量
  • $! — 类似于errno的数字,但是像strerror(errno)一样是一个字符串
  • $_隐形变量,总是被使用但从来没有显示出来
  • $#_ — 最后一个子例程参数的索引号...也许
  • @_ — 当前函数的(非)名称...也许
  • $@ — 最近抛出的异常
  • %:: — 符号表
  • $:$^$~$-$= — 与输出格式有关的东西
  • $.$% — 输入行号、输出页码
  • $/$\ — 输入和输出记录分隔符
  • $| — 输出缓冲控制器
  • $[ — 将数组基数从0-based改为1-based或42-based:WHEEE!
  • $}奇怪地什么也没有!
  • $<$>$($) — 真实和有效的UID和GID
  • @ISA — 当前包的直接超类的名称
  • $^T — 脚本启动时间(时代秒)
  • $^O — 当前操作系统名称
  • $^V — Perl版本号

还有更多变量可以使用。在这里阅读完整列表。


83
"$[" 变量是所有变量中最邪恶的。 - Brad Gilbert
24
如果我不用每五秒钟查看 perldoc perlvar,就能编写 Perl 6 代码,那肯定会很感激。(尽管我承认有一半的时间我检查它时想着,“我知道有一个特殊变量可以为我完成这个任务,但我不记得是哪一个…” =P) - Chris Lutz
17
use English; 的问题在于它会影响正则表达式的性能。这不是我编造的。详情请见:http://perldoc.perl.org/English.html#PERFORMANCE。 - David Webb
13
@Dave:你所提供的页面中有“-no_match_vars”选项,所以这不是一个问题。@Brad:$[非常糟糕。尽管它的目的也很糟糕,但它甚至都不能正常工作!@Artem:根据perlvar中的描述,“以数字、控制字符或标点符号开头的Perl标识符不受包声明的影响,始终强制处于main包;它们还免于strict 'vars'错误。” 这意味着即使在严格模式下,@$也可以被创建和分配,而不会出现错误。 唉! - rjh
4
@Brian: 当官方文档本身声明Perl解释器在某些情况下启发式地猜测一系列字符的含义时,你打算如何学习语法呢?例如,在 /$foo[bar]/中,[bar]部分是字符类还是数组@foo的下标?请查看perldata中的Grep以获取令人恐惧的答案。 - j_random_hacker
显示剩余18条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接