c语言 - 你能解释一下while(*++str1)和return (str1 - str2)的作用吗?



在这种情况下,while循环是否像for循环一样工作?另外,str1-str2字符串减法会导致什么?

#include <stdio.h> 
int fun(char *str1) { 
char *str2 = str1; 
while (*++str1);   
return (str1 - str2); 
} 
int main() { 
char *str = "GeeksQuiz"; 
printf("%d", fun(str)); 
return 0; 
} 

请注意,您在这里使用的是指针而不是字符串,因此从末尾开始,str1-str2是指针算术。 如您所知,字符串应该以 null 结尾,因此在内存中"GeeksQuiz"实际上是一个具有下一个值的字符数组:GeeksQuiz\0。这样,while(*++str1);将遍历此数组的值,直到它达到 \0。 总而言之,此函数将返回字符串中的字符数。

该函数的目的是计算字符串的长度。那就是这个同时循环

while(*++str1);

迭代,直到遇到终止零字符''。假设在 while 循环之后,指针str1将指向终止的零字符''而指针str2由于初始赋值而指向字符串的开头

char *str2 = str1; 

因此,str1-str2差将产生字符串的长度。字符串的长度确定为字符串中终止零字符''之前的字符数。

但是,该函数有一个错误。 如果用户将传递一个空字符串"",该字符串在内部表示为字符数组,其中一个元素等于终止零字符{ '' }则函数将调用未定义的行为。因此,空字符串在其第一个字符中包含终止零字符''。 但是,在 while 循环中,指针首先str1递增,然后已经检查下一个字符是否是终止零字符''

那就是这个同时循环

while(*++str1);   

可以按以下方式重写

while ( ( ++str1, *str1 != '' ) );   

正如最初看到的那样,指针 str1 递增。

除了这个缺陷之外,函数参数应该具有限定符const因为在函数中传递的字符串没有被更改。此外,函数的返回类型应该是无符号整数类型,例如size_t(它是执行相同任务的标准 C 字符串函数strlen的返回类型。

该函数可以通过以下方式声明为定义

size_t fun( const char *s ) 
{ 
const char *t = s;

while( *t ) ++t;

return t - s; 
} 

这是一个演示程序。

#include <stdio.h>
size_t fun( const char *s ) 
{ 
const char *t = s;

while( *t ) ++t;

return t - s; 
} 
int main(void) 
{
const char *s = "";
printf( "The length of the string "%s" is equal to %zun", s, fun( s ) );

s = "1";
printf( "The length of the string "%s" is equal to %zun", s, fun( s ) );
s = "12";
printf( "The length of the string "%s" is equal to %zun", s, fun( s ) );
s = "123";
printf( "The length of the string "%s" is equal to %zun", s, fun( s ) );
return 0;
}

程序输出为

The length of the string "" is equal to 0
The length of the string "1" is equal to 1
The length of the string "12" is equal to 2
The length of the string "123" is equal to 3

循环while (*++str1);递增指针,读取更新str1指向的字节,并测试此字节是否为空,否则它将停止,否则什么都不做并重复。使用显式语句而不是空语句;,此循环将更具可读性:

while (*++str1 != '')
continue;

return (str1 - str2);计算指针str1str2的差值,并将此值作为int返回。如果 2 个指针指向同一数组并计算它们之间的元素数,则定义

它们之间的差异。该函数尝试计算字符串参数的长度,但对于空字符串会失败,因为str1总是在测试之前递增,因此会在空字符串的偏移量0处跳过 null 终止符。该行为未定义,因为代码随后读取到字符串末尾之外。对于非空字符串,它打印非空字符的数量,即字符串的长度:fun("GeeksQuiz")返回 9。

这是一个修改版本:

#include <stdio.h> 
int fun(const char *str) { 
const char *start = str;
while (*str != '')
str++;
return str - start; 
} 
int main() { 
const char *str = "GeeksQuiz"; 
printf("length of "%s" is %dn", str, fun(str)); 
return 0; 
} 

我通过clang-format运行了你的代码[见注释1],以确认我对你在那里发生的奇怪的while循环的怀疑,我想出了这个作为你的代码的正确格式:

#include <stdio.h>
int fun(char *str1)
{
char *str2 = str1;
while (*++str1)
;
return (str1 - str2);
}
int main()
{
char *str = "GeeksQuiz";
printf("%d", fun(str));
return 0;
}

就个人而言,我会这样写,以使while循环变得超级明显。您可以在此处运行此代码:https://onlinegdb.com/BkxlKb75GO。

#include <stdio.h>
int fun(char *str1)
{
char *str2 = str1;
while (*++str1)
{
// do nothing 
}
return (str1 - str2);
}
int main()
{
char *str = "GeeksQuiz";
printf("%d", fun(str));
return 0;
}

然而,更具可读性的是fun()函数,我也将其重命名为count_num_chars_in_str()

int count_num_chars_in_str(char *str1)
{
char *str2 = str1;
while (*str1 != '')
{
str1++;
}
return str1 - str2;
}

较短的名称可能是num_chars_in_str()str_length()strlen()strlen()已经存在(见这里和这里),这正是它的作用。它可以包含在标头string.h中,并且是C和C++标准的一部分。以下是它在 cplusplus.com 上的描述:

size_t strlen ( const char * str );
获取字符串长度
返回 C 字符串 str 的长度。

C 字符串的长度由终止空字符确定:C 字符串的长度与字符串开头和终止空字符之间的字符数一样长(不包括终止空字符本身)。

这不应与保存字符串的数组的大小混淆。

因此,运行上述任何这些程序,输出都是9.程序所做的只是计算传入的字符串中非空字符的数量(其中0空字符或''--相同的东西),在这种情况下GeeksQuizGeeksQuiz包含 9 个非空字符。

顺便说一下,你从哪里得到你的代码?请发布链接和参考资料。您应该始终引用您的来源。

while (*++str)只是一次递增一个字符的str指针,直到找到一个空终止符(0),它发生在字符串的末尾,在字符串中最后一个字符之后。一旦发生这种情况,就会取两个字符指针之间的差异,从而导致 null 终止符紧z之后的地址位置与字符串中第一个字符的地址位置之间的差异,即G。这 2 个字符之间的内存地址差异为 9 个字符。

原始版本不仅可读性较差,而且其中还有一个错误。对于此测试用例,它应该打印0,但它打印1

char *str = "";
printf("%d", fun(str));

我在count_num_chars_in_str()中更具可读性的版本也纠正了此错误。

教训:不要编写不可读或混淆的代码。


[Note 1] 我运行它的方式clang-format是我只是将您的原始代码复制粘贴到main.c文件中,然后将其复制到我的eRCaGuy_CodeFormatter存储库中,然后运行./run_clang-format.sh

在这种情况下,while循环是否像for循环一样工作?

我会说所有while循环都像for循环,反之亦然。

任何时候你有一个循环

while(condition)
{ /* do something */; }

您可以将其替换为等效的for循环:

for(; condition; )
{ /* do something */; }

反其道而行之,任何时候你有一个for循环

for(initial_expression; test_expression; increment_expression)
{ /* do something */; }

您可以(几乎)将其替换为等效的while循环:

initial_expression;
while(test_expression) {
/* do something */;
increment_expression;
}

(两者之间有一个很小的差异,但只有在循环中使用continue语句时才会显示。

如果你被困在一个C编译器坏了的荒岛上(或者如果你被困在一个喜欢提出"技巧"问题的教师的教室里),并且你必须在不使用for关键字的情况下编写一个C程序,你可以:你可以使用while来编写所有的循环, 而不会丢失功能。

相关内容

最新更新