【问题标题】:Can you explain what while(*++str1) and return (str1 - str2) does?你能解释一下 while(*++str1) 和 return (str1 - str2) 的作用吗?
【发布时间】:2021-05-30 18:01:12
【问题描述】:

在这种情况下,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; 
} 

【问题讨论】:

  • 查看How do I ask a good question? 然后查看edit 问题以包含您需要帮助的自己的工作。
  • 从另一个指针中减去一个指针得到分隔它们的 元素 的数量,但它们必须都指向同一个 object (或过去的一个元素它)是有效的算术。类似于数组索引,其中array[42] 是第 43 个元素,不一定是第 43 个字节
  • 如果你从某个地方复制了这段代码,我几乎 100% 肯定你这样做了,因为字符串是 GeeksQuiz请通过将 URL 粘贴到你的问题。

标签: c while-loop c-strings strlen function-definition


【解决方案1】:

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

【讨论】:

  • "将返回字符串中的字符数" 如果str1 指向空字符串,则为UB。
  • UB 代表什么?
  • 未定义行为@WolfgangRoth
  • 这个函数会返回字符串中的字符数。恐怕不会。
【解决方案2】:

我通过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'--相同的东西),在这种情况下是GeeksQuizGeeksQuiz 包含 9 个非空字符。

顺便说一句,你的代码是从哪里得到的?请张贴链接和参考。您应该始终参考您的来源。

while (*++str) 只是不断增加str 指针一个字符,直到找到一个空终止符(0),它出现在字符串的末尾,在它的最后一个字符之后。一旦发生这种情况,就会取两个 char 指针之间的差异,从而导致在 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

【讨论】:

  • (*str1 != NULL) 不正确。 NULL 是一个空指针,而不是一个空字节。请改用(*str1 != '\0')。您应该只对空指针使用 NULL,并在 C 字符串的末尾为 '\0' 写入 null 终止符null byte
  • @chqrlie,已修复。我想NULL 通常被定义为零但不一定是,所以其他选项更正确。而且,NULL 旨在用于指针,而不是用于指针指向的内容(除非它也是指针:))。
  • 确实 NULL 可以定义为 ((void *)NULL) 并且必须在 void *int 具有不同表示形式(例如:sizeof(void*) != sizeof(int))或传入的架构上如此定义可变参数函数或没有原型的函数的不同方法。
  • 您可能还提到 fun("\0") 返回 1fun("") 具有未定义的行为并解释差异。
【解决方案3】:

循环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; 
} 

【讨论】:

    【解决方案4】:

    该函数的目的是计算字符串的长度。就是这个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", s, fun( s ) );
    
        s = "12";
        printf( "The length of the string \"%s\" is equal to %zu\n", s, fun( s ) );
    
        s = "123";
        printf( "The length of the string \"%s\" is equal to %zu\n", 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
    

    【讨论】:

      【解决方案5】:

      在这种情况下,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 程序987654333@ 关键字,您可以:您可以改用while 编写所有循环,而不会丢失功能。

      【讨论】:

        猜你喜欢
        • 2010-09-16
        • 2019-12-15
        • 1970-01-01
        • 1970-01-01
        • 2013-11-13
        • 1970-01-01
        • 2011-09-11
        • 1970-01-01
        • 2015-05-14
        相关资源
        最近更新 更多