这是我不理解的代码,它只是反转一个字符串.
#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.
因此,让我们看看代码在p
null时的作用.
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
.这种古怪的目的是什么?代码的目的不是要显示你知道多少运算符,首先是与代码的读者进行通信.
该函数还修改了其形式参数,这使得调试变得困难.
代码
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循环中崩溃.(但它仍会在第二个循环中崩溃).