C指针算术

时间:2014-01-21 15:39:10

标签: c pointers

这是我不理解的代码,它只是反转一个字符串。

#include <stdio.h>

void strrev(char *p)
{
  char *q = p;
  while(q && *q) ++q;
  for(--q; p < q; ++p, --q)
    *p = *p ^ *q,
    *q = *p ^ *q,
    *p = *p ^ *q;
}

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]); strrev(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}

我不理解的唯一一段代码就是这一段:while(q && *q) ++q;,它用于查找eos。 是不是与while(*q) ++q;相同,因为q永远不会是0?代码的作者如何确保q*q为0?

此代码来自此问题:How do you reverse a string in place in C or C++?

3 个答案:

答案 0 :(得分:29)

David Heffernan的评论是正确的。该代码是令人震惊的

您要问的代码的一点是,如果它为null,则跳过取消引用q。因此,代码的作者认为q可以为null。在什么情况下q可以为空?最明显的是:如果p为空。

因此,让我们看看p为空时代码的作用。

void strrev(char *p) // Assumption: p is null
{
  char *q = p; // now q is null
  while(q && *q) ++q; // The loop is skipped, so q and p are both still null.
  for(--q; 

所以我们做的第一件事是递减q,它是null。可能这将包围,我们将得到包含最大可能指针的结果q。

    p < q; 

由于null小于除null之外的所有内容,并且q不再为null,因此这是真的。我们进入循环......

    ++p, --q)
    *p = *p ^ *q,

及时取消引用null。

    *q = *p ^ *q,
    *p = *p ^ *q;
}

顺便说一句,在Coverity,我们将此称为“前向空缺陷” - 即代码路径指示值可能为null的模式,然后在相同的代码路径上假定它不是空的。这是非常普遍的。

好的,如果将null作为参数,则此代码完全被破坏。还有其他方法可以打破吗?如果我们给它一个空字符串会发生什么?

void strrev(char *p) // Assumption: *p is 0
{
  char *q = p; // *q is 0
  while(q && *q) ++q; // The second condition is not met so the body is skipped.
  for(--q; // q now points *before valid memory*.
       p < q  // And we compare an invalid pointer to a valid one.

我们在C中有一个保证,即当你从指向有效内存的指针中减去一个,然后将该指针与另一个指针进行比较时,该比较是否合理?因为这让我觉得非常危险。我不太清楚C标准是否足以说明这是否是未定义的行为。

此外,这段代码使用了可怕的“用xor交换两个字符”技巧。 为什么人们会这样做?它会生成更大,更慢的机器代码,更难以阅读,理解和维护。如果你想交换两件事,交换它们

它还使用逗号运算符将多个语句放在一个语句中,以避免for主体周围的括号恐怖。这种古怪的目的是什么?代码的目的不是要显示你知道多少运算符,首先是与代码的读者进行通信

该函数还修改了其形式参数,这使得调试变得困难。

答案 1 :(得分:5)

代码

while(q && *q)

的简写
while(q != NULL && *q != '\0')

因此,您正在测试q(开头等于p)是否为NULL。这意味着使用NULL参数调用的函数不会在此while循环中崩溃。 (但它仍然会在第二个循环中崩溃)。

答案 2 :(得分:1)

  

它与while(*q) ++q;相同,因为q永远不会是0吗?

while(q && *q)用于确保q不是NULL*q不是NUL。如果while(*q)未指向q,则使用NULL也是合法的。

void string_rev(char *p)
{
   char *q = p;

   if(q == NULL)
       return;

   while(*q) ++q;
   for(--q; p < q; ++p, --q)
       *p = *p ^ *q,
       *q = *p ^ *q,
       *p = *p ^ *q;

}  
  

代码的作者如何确保q*q将成为0

如果while(q && *q)指向q并且循环在进入循环体之前终止,则

NULLp可以是NULL指针。否则,通过该操作,它不能是NULL指针。现在循环终止取决于*q。在q到达字符串末尾后,*q变为0并且循环终止。