你能解释一下 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 是指针算术。 如您所知,字符串应以空值结尾,因此在内存中“GeeksQuiz”实际上是一个字符数组,其具有下一个值:GeeksQuiz\0。这样,while(*++str1); 将遍历此数组的值,直到到达 \0。 总而言之,此函数将返回字符串中的字符数。

,

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

while(*++str1);

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

char *str2 = str1; 

所以差值 str1-str2 将产生字符串的长度。字符串的长度确定为字符串中终止零字符 '\0' 之前的字符数。

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

这就是while循环

while(*++str1);   

可以改写成以下方式

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

正如最初看到的,指针 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 %zu\n",s,fun( s ) );
    
    s = "1";
    printf( "The length of the string \"%s\" is equal to %zu\n",fun( s ) );

    s = "12";
    printf( "The length of the string \"%s\" is equal to %zu\n",fun( s ) );

    s = "123";
    printf( "The length of the string \"%s\" is equal to %zu\n",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
,

我通过 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 != '\0')
    {
        str1++;
    }
    return str1 - str2;
}

较短的名称可能是 num_chars_in_str()str_length()strlen()strlen() 已经存在(see herehere),这正是它所做的。它可以包含在标头 string.h 中,并且是 C 和 C++ 标准的一部分。这是它的描述on cplusplus.com

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

C 字符串的长度由终止空字符决定:AC 字符串等于字符串开头和终止空字符之间的字符数(不包括终止空字符本身)。

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

因此,运行上述任何程序,输出为 9。程序所做的就是计算传入的字符串中非空字符(其中空字符是 0'\0'--相同的东西)的数量,即 GeeksQuiz这个案例。 GeeksQuiz 包含 9 个非空字符。

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

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

原始版本不仅可读性差,而且还存在错误。对于这个测试用例,它应该打印 0,但它打印的是 1

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

我在 count_num_chars_in_str() 中更易读的版本也修正了这个错误。

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


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

,

循环 while (*++str1); 递增指针,读取更新后的 str1 指向的字节,并测试该字节是否为空,如果不是则停止,否则什么也不做并重复。使用显式语句而不是空语句 ;:

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

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

该函数尝试计算字符串参数的长度,但对于空字符串会失败,因为 str1 总是在测试前递增,因此对于空字符串会跳过偏移量 0 处的空终止符细绳。该行为是未定义的,因为代码然后读取超出字符串的末尾。对于非空字符串,它打印非空字符的数量,也就是字符串的长度:fun("GeeksQuiz") 返回 9。

这是修改后的版本:

#include <stdio.h> 

int fun(const char *str) { 
    const char *start = str;
    while (*str != '\0')
        str++;
    return str - start; 
} 

int main() { 
    const char *str = "GeeksQuiz"; 
    printf("length of \"%s\" is %d\n",str,fun(str)); 
    return 0; 
} 
,

在这种情况下,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 编译器损坏的荒岛上(或者你被困在一个喜欢提出“诡计”问题的教师的教室里),并且你不得不编写一个 C 程序而不使用 { {1}} 关键字,您可以:您可以使用 for 编写所有循环,而不会丢失功能。