【问题标题】:Ternary operator as a command?三元运算符作为命令?
【发布时间】:2015-12-13 20:48:44
【问题描述】:

source-code for nanodns 中,有一个非典型的使用三元运算符来试图减小代码大小:

/* If the incoming packet has an AR record (such as in an EDNS request),
 * mark the reply as "NOT IMPLEMENTED"; using a?b:c form to save one byte*/
q[11]?q[3]|=4:1;

这条线的作用并不明显。乍一看,它看起来像是在为两个数组元素之一赋值,但事实并非如此。相反,它似乎要么是一个数组元素,要么什么都不做(运行“命令”1)。

看起来它应该是这行代码的替代品(确实长了一个字节):

if(q[11])q[3]|=4;

字面意思是这样的:

if (q[11])
  q[3]|=4;
else
  1;

三元运算符通常用作表达式的一部分,因此将其用作独立命令似乎很奇怪。再加上看似格格不入的1,这行代码几乎可以称得上是混淆代码了。

我做了一个快速测试,能够编译和运行一个 C(++) 程序,其中数据常量作为“命令”,例如 void main() {0; 'a'; "foobar"; false;}seems to bea sort of nop command,但我找不到任何关于这种用法的信息——Google isn’tvery amenableto thistype ofsearchquery)。

谁能准确解释它是什么以及它是如何工作的?

【问题讨论】:

  • q[11],而不是q[1]。你的分析是正确的;没有更大的上下文,它相当于if (q[11]) { q[3] |= 4; },只是更短。
  • 相当于if ( q[11] ) q[3] |= 4;
  • “这一行几乎可以说是混淆代码”——确实
  • “谁能准确解释它是什么以及它是如何工作的?” - 你的问题正确地描述了这两个方面。
  • 这是糟糕的代码。使用q[11]&&(q[3]|=4);

标签: c++ c ternary-operator deobfuscation


【解决方案1】:

请注意,您链接到的代码是糟糕,并且是由一个非常糟糕的程序员编写的。特别是声明

"在微型 C 程序中定义重用表达式是一种常见的做法 让代码更小”

是完整的b***s***。这句话是事情开始出现严重错误的地方。

源代码的大小与编译器可执行文件的大小无关,与可执行文件的内存消耗无关,也与程序性能无关。它唯一影响的是程序员计算机上源代码文件的大小,以字节表示。

除非您在 80 年代中期的某些 8086 计算机上进行编程,并且硬盘空间非常有限,否则您永远不需要“减少代码的大小”。相反,编写可读的代码。

话虽如此,由于q 是一个字符数组,因此您链接的代码相当于

if(q[11])
{
  (int)(q[3] |= 4);
}
else
{
  1;
}

1 是一个没有副作用的语句,它将被优化掉。之所以把它放在那里,是因为?: 运算符需要第三个运算符。

if 语句和?: 运算符之间的唯一区别是微妙的:?: 隐式平衡了第二个和第三个操作数之间的类型。

为了提高可读性并生成自文档化代码,代码应该重写为类似

if (q[AR_INDEX] != 0)
{
  q[REPLY_INDEX] |= NOT_IMPLEMENTED;
}

附带说明,这里有一个错误:q[2]|=128;q 是 char 类型,它具有实现定义的符号,所以这条线可能是灾难性的。核心问题是你永远不应该将char 类型用于按位运算或任何形式的算术,这是一个典型的初学者错误。必须替换为uint8_tunsigned char

【讨论】:

  • 特别具有讽刺意味的是,如果代码的作者非常担心源代码的大小,以至于他们选择使用混淆形式的语句来保存一个字节,然后他们会在评论中添加using a?b:c form to save one byte -- 这 33 个字节可能会清除由晦涩代码保存的所有内容!
  • @TripeHound,即代码的扩展、解释版本。 “minified” version 不包含任何 cmets(除了一开始解释许可证的可选 cmets)。
  • @Lundin,该特定代码仅用于作为最小的 DNS 服务器。他们的目标是“minify”fuller version,就像代码高尔夫一样。但是很好地解决了这个错误,感谢您的解释。
  • @Synetech 仍然没有解释为什么他们会减少源代码本身。实际上,编写代码的人似乎并不了解源代码大小和二进制可执行文件大小之间的区别。
【解决方案2】:

在 C 和 C++ 中,任何表达式都可以通过将 ; 放在末尾来组成语句。

另一个例子是表达式 x = 5 可以做成一个语句:x = 5;。希望您同意这是一个好主意。

如果试图“禁止”某些表达式子集让 ; 出现在它们之后,这将不必要地使语言复杂化。此代码不是很有用,但它是合法的。

【讨论】:

  • 不仅仅是复杂——一堆函数会变得几乎无法使用,比如printf——你能想象每次都写int outputted = printf("...");吗? :)
  • In C and C++ any expression can be made into a statement by putting ; at the end. 当然,POD 类型(“内置”)都是表达式……这是有道理的。当然,它们不一定意味着什么,对吧?我想你需要知道混淆代码高尔夫的这些细节。我原以为需要专门编写对象以支持用作表达式,但显然它们没有;这是内置的。我刚刚使用myclass; 进行了测试。它几乎让我想起了 Python 和 Ruby 等高级语言,其代码类似于 1.to_s
猜你喜欢
  • 2012-04-02
  • 1970-01-01
  • 2016-06-28
  • 2021-11-13
  • 2018-09-18
  • 1970-01-01
  • 2013-07-12
  • 2018-09-10
相关资源
最近更新 更多