C指针算术

AR8*_*R89 8 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;
}
Run Code Online (Sandbox Code Playgroud)

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

此代码来自这个问题:如何在C或C++中反转字符串?

Eri*_*ert 30

David Heffernan的评论是正确的.那段代码令人震惊.

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

因此,让我们看看代码在pnull时的作用.

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; 
Run Code Online (Sandbox Code Playgroud)

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

    p < q; 
Run Code Online (Sandbox Code Playgroud)

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

    ++p, --q)
    *p = *p ^ *q,
Run Code Online (Sandbox Code Playgroud)

并立即取消引用null.

    *q = *p ^ *q,
    *p = *p ^ *q;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,在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.
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • C对比较指针的保证是,你可以安全地以有意义的方式这样做,只要两者都指向同一个对象,或指向同一聚合对象的成员.作为一种特殊情况,指向数组最后一个元素的指针被认为是数组的一部分,用于比较目的,但绝不能实际取消引用.在数组开头之前指向一个的指针是Undefined Behavior. (5认同)
  • 有关C有关添加,减去和比较指针的详细信息,请参阅N1570,§6.5.6附加运算符,第111页的pdf第8段(标记为第93页),§6.5.8关系运算符,段落第4页和第5页在pdf第113/114页(标记为第95/96页)和§6.5.9平等操作员,第5和第6段. (4认同)
  • +1代码审查的地狱._appalling_在我读完之后似乎是一种恭维:) (2认同)

Woj*_*wka 5

代码

while(q && *q)
Run Code Online (Sandbox Code Playgroud)

是一个简写

while(q != NULL && *q != '\0')
Run Code Online (Sandbox Code Playgroud)

所以你正在测试q(在开头等于p)是不是NULL.这意味着使用NULL参数调用的函数不会在此while循环中崩溃.(但它仍会在第二个循环中崩溃).

  • 不,问题是while(q &&*q)和while(*q)是否相同. (2认同)