这里可以调用 inDiv 来搜索c
字符串中的字符divs
,例如:
inDiv('x', "is there an x character in here somewhere?') will return 1
inDiv('x', "ahhh... not this time') will return 0
通过它工作:
int inDiv(char c, char * divs)
{
char * p_divs = divs; // remember which character we're considering
char tmp;
while(tmp = *p_divs++) // copy that character into tmp, and move p_divs to the next character
// but if tmp is then 0/false, break out of the while loop
if (tmp == c) return 1; // if tmp is the character we're searching for, return "1" meaning found
return 0; // must be here because tmp == 0 indicating end-of-string - return "0" meaning not-found
}
reverse
我们可以通过查看调用站点来推断:
int main()
{
char source[MAS_SIZE], dest[MAS_SIZE], divs[MAS_SIZE];
printf("String : ");
gets(source);
printf("Dividers : ");
gets(divs);
reverse(source, dest, divs);
printf("Reversed string : %s", dest);
我们可以看到gets()
从标准输入读取到字符数组的调用,source
然后divs
-> 将这些输入提供给reverse()
. 打印出来的方式dest
,显然是要作为字符串反转的目的地source
。在这个阶段,没有洞察力的相关性divs
。
让我们看看源...
void reverse(char * source, char * dest, char * divs)
{
*dest = '\0'; //what does this pointer do?
int source_len = strlen(source); //what is source
if (source_len == 0) return;
char* p_source = source + source_len - 1;
char* p_dest = dest;
while(p_source >= source)
{
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
在这里,*dest = '\0'
将一个 NUL 字符写入字符数组dest
- 这是编码字符串结尾位置的正常标记值 - 将它放在第一个字符处*dest
意味着我们希望清除目标。我们知道source
我们将要反转的文本输入 -strlen()
将设置source_len
为其中的字符数。如果没有字符,那么return
因为没有工作要做并且输出已经以 NUL 终止。否则,将p_source
创建一个新指针并将其初始化为source + source_len - 1
->,这意味着它指向源代码中的最后一个非 NUL 字符。 p_dest
指向目标缓冲区开头的 NUL 字符。
然后循环说:while (p_source >= source)
- 为此做任何事情p_source
必须首先是- 作为最后一个字符的点并且是缓冲区中的第一个字符地址是>= source
有意义的;比较意味着我们将一个或两个移向另一个,直到它们交叉 - 每次都做一些工作。这使我们:p_source
source
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
这是我们刚刚看到的相同测试 - 但这次我们只是p_source
向后移动到字符串的开头,而inDiv(*p_source, divs)
这也是正确的......这意味着字符 at*p_source
是字符串中的字符之一divs
。它的意思基本上是:向后移动,直到你超过了字符串的开头(尽管这个测试有未定义的行为,正如 Michael Burr 在评论中指出的那样,如果字符串恰好被分配在地址 0 上,真的可能不起作用 -即使相对于某些特定的数据段,因为指针可以从 0 变为类似于 FFFFFFFF 十六进制的值,而不会看起来小于 0),或者直到您找到一个不是“分隔符”字符之一的字符。
在这里,我们对代码的作用有了一些真正的了解......将输入分成由输入中的任何一组字符分隔的“单词” divs
,然后将它们以相反的顺序用空格分隔符写入目标缓冲区。这有点超前了 - 但让我们跟踪一下:
下一行是...
if (p_source < source) break;
...这意味着如果循环退出时已超过源字符串的前面,则退出所有循环(向前看,我们看到代码只是在已经生成的输出的末尾放置了一个新的 NUL 并返回- 但这是我们所期望的吗? - 如果我们一直通过“hello world”中的“hello”支持,那么我们会点击字符串的开头并终止循环,而不会将最后一个“hello”单词复制到输出!输出将始终是输入中的所有单词 - 除了第一个单词 - 反转 - 这不是作者描述的行为)。
否则:
char* w_end = p_source; // remember where the non-divider character "word" ends
// move backwards until there are no more characters (p_source < source) or you find a non-divider character
while((p_source >= source) && (!inDiv(*p_source, divs))) p_source--;
// either way that loop exited, the "word" begins at p_source + 1
char * w_beg = p_source + 1;
// append the word between w_beg and w_end to the destination buffer
for(char* p = w_beg; p <= w_end; p++) *p_dest++ = *p;
// also add a space...
*p_dest++ = ' ';
输入中的每个“单词”都会发生这种情况,然后最后一行将 NUL 终止符添加到目的地。
*p_dest = '\0';
现在,你说:
根据 [to] 作者,它写了一个 hello world 字符串,然后其中有一个函数,它也将字符串反转为 world hello
好吧,给定输入“hello world”和包含空格的分隔符(但输入中没有其他字符),那么输出将是“hello world”(注意末尾的空格)。
对于它的价值 - 这段代码还不错......对于 ASCIIZ 缓冲区的 C 处理来说是很正常的,尽管关于输入长度的假设是危险的并且它丢失了第一个单词......
** 如何修复未定义的行为 **
关于未定义的行为 - 对地址的最小更改是更改循环,以便它们在缓冲区开始时终止,并让下一行明确检查它终止的原因并确定需要什么行为。那会有点难看,但不是火箭科学……