C指针算术代码片段

4
我有一个程序需要破解。它是从另一种语言(这里不方便透露名称)翻译成C的。为了理解其工作原理,我正在慢慢重写和简化代码,以使用C提供的所有好的逻辑结构。
下面这段小代码在我的代码中不断出现,其中X和Y的值各不相同:
ptr[X]--;
while(ptr[X])
  {
    ptr[X]--;
    ptr += Y;
  }

ptr 的类型是 char *,由于它深嵌在循环中且依赖于输入和输出,我无法对数组的状态进行假设。我可以成功地“简化”为:

for(ptr[X]--; ptr[X]; ptr[X]--, ptr += Y);

但那太糟糕了。稍微好一点的说法是:

for(ptr[X]--; ptr[X]; ptr += Y) ptr[X]--;

我想知道是否有人能提供更好的上述代码简化版本,我将非常感激。这种情况不止发生了五次,并且妨碍了我简化和理解流程控制的能力,因此,如果有人能提供更简洁/易读的版本,那将是太棒了。如果有人能为代码提供任何精彩的见解,那也很棒,尽管我基本上理解它的作用。
对于特定的X和/或Y代码的洞察,也可以有所帮助。就其价值而言,Y往往介于-2和2之间,而X通常为1。

1
有人能解释一下“这里未讨论的语言”在远程实际应用中的用途吗? - Tom
也许这是一个关于指针操作的练习吧? - poundifdef
@Tom - 娱乐?我写了一个解释器来学习C语言,这对我很有帮助。 - Chris Lutz
@Chris。我只是在问而已。我第一次了解它还没有那么久,一直很好奇它的用途。谢谢你分享你的经验。 - Tom
好的。如果我可以添加一些有趣的观点,一个基本的cat实用程序就像",[.,]"这样简单。这个特定的程序实际上是一个bf解释器,用bf编写的,我想了解它的工作原理,因为这是我能想象到的唯一不使用递归编写的解释器(而且我已经知道如何使用递归编写一个)。 - Chris Lutz
显示剩余2条评论
4个回答

8

ptr[X] 相当于 *(ptr + X),因此我们可以将其重写为以下形式:

for((*(ptr + X))--; *(ptr + X); (*(ptr + X))--, ptr += Y);

现在这里有很多冗余,所以我们可以简化为:
char *ptr_plus_x = ptr + X;
for((*ptr_plus_x)--; *ptr_plus_x; (*ptr_plus_x)--, ptr_plus_x += Y);

那么我们可以完全去掉 ptr_plus_x

ptr += X;
for((*ptr)--; *ptr; (*ptr)--, ptr += Y);

在英语中,我们访问偏移量为X、X+Y、X+2Y、X+3Y等的内存位置,逐个递减每个内存位置的值,直到找到一个值为0的内存位置。但是,0的测试总是在递减后发生,因此我们实际上是在寻找该序列中第一个值为1的内存位置。一旦找到它,我们将其递减为0并退出。

如果Y为1,则我们向前连续递减一系列内存位置,直到第一个1(包括该位置)为止。如果Y为-1,则从X偏移量开始向后搜索,同样的操作。如果Y为0,则会发生无限循环。如果Y是其他值,则搜索模式会跳过各种条目。

这不是非常直观的功能,所以我可以理解您为什么感到困惑。


要么比我们俩想的更令人困惑,要么就是在没有上下文的情况下很难看。这个版本给我一个总线错误。我以为通过在for()循环的末尾添加ptr -= X来纠正它,这将消除总线错误,但它不起作用(不产生输出)。 - Chris Lutz
我建议使用预减法而不是后减法,并在循环指令之后添加指令ptr -= X,以将ptr变量恢复到与原始代码完全相同的状态。当然,除非没有必要。 - chmike
如果Y=0,则不会出现无限循环。它只会使当前位置为0(或下溢),而不会向前或向后移动。 - aib

3
我会加上:

ptr[X]--
while (ptr[X]--) ptr+=Y;

先进行评估,然后减少(对于while条件来说)

编辑:好吧,明天我会讨厌自己。在这个级别上使用goto是可以的,对吗?

dec:  ptr[x]--
      while (ptr[X]){
           ptr+=Y;
           goto dec;
      }

我真的不知道是留下这个还是删除它。

编辑2:那么这个怎么样?(TCC没有抱怨)

 while (ptr[X]--?ptr[X]--,ptr+=Y:0){} 

编辑2 1/2;
  //longshot
  while (ptr[X]--?ptr[X]--,ptr+=Y, ptr[X]:0){} 

如果其他方法都失败了...
编辑3:今晚最后一条。
while (ptr[X]--?ptr[X]--,ptr+=Y:0){
      if (!ptr[X]) break;
 }//good luck with this, it has been very amusing.

这是我尝试的第一件事之一,但它给了我一个总线错误。值得一提的是,在结尾添加 ptr[X]++ 确实可以工作,所以这只是一个偏移量错误。 - Chris Lutz
不,后置条件是错误的,因为您从最终ptr[X]中减去了太多。 - paxdiablo
关于goto:放弃吧。它并不完全起作用(需要在开头加上额外的 ptr[X]--),但却非常有趣。 - Chris Lutz
1
我曾希望三元运算符能结束这场辩论。 - Tom
现在三元运算符不再产生总线错误,而是无限循环。我认为三元解决方案中有太多的-ing。 - Chris Lutz
显示剩余4条评论

2

这个不得而知的IT网站宣称:

The semantics of the it-which-shall-not-be-named states commands can also
be succinctly expressed in terms of C, as follows (assuming that p has 
been previously defined as a char*):

>   becomes     ++p;
<   becomes     --p;
+   becomes     ++*p;
-   becomes     --*p;
.   becomes     putchar(*p);
,   becomes     *p = getchar();
[   becomes     while (*p) {
]   becomes     }

所以似乎将其转换为C应该相当容易。

编辑:这里是Hello World BF转换为C++的代码。


我已经过了那一步了。我使用了同样基本图表的优化版本,所以++++被翻译成了*p += 4;。现在我正在尝试将代码逻辑减少到更可用的程度。 - Chris Lutz
你可以发布一下那个将要被翻译成循环的 BF 代码片段吗? - xian
它会类似于">X-[->Y]<X",但将X和Y更改为X或Y个>或<。 "-[-<]"(直接来自源代码),其中X为0。(X已从原始翻译中更改,因为我一直在处理代码,但我验证简化的代码是否按预期执行,因此它应该在功能上等效。) - Chris Lutz

0

已经很简单了。与其试图写更少的语句,我宁愿尝试理解意图并添加一些注释。

代码片段中'a'的一个含义示例:减小矩阵Y列中某一列(X)的所有元素。例如,在没有直接赋值的语言中,您需要这样做才能绘制一条垂直的“+”线。

您可以通过直接显示索引来澄清此含义:

// set elements of column to cGoal
for( int decrementsToGoal = cGoal; decrementsToGoal != 0; --decrementsToGoal ) {
    // decrease all elements of column X
    for( int row = cMaxRows; M[ row*matrixsizeY + columnX ]; --row ) {
        --M[ row*matrixsizeY + columnX ];
    }
}

祝你好运 :)


我并不一定想要减少语句的数量,我只是想简化逻辑。特别是,我遇到了一个问题,就是循环同时影响指针和指针所指向的对象,这让我无法像简化代码其他部分那样简化它。 - Chris Lutz
我想xtofl可能从我的话语中得到了那个想法。我只是在开玩笑 :) - Tom
@Tom:你的代码几乎和 Perl 脚本一样难以阅读 :P - xtofl

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