这个C代码如何工作?

时间:2009-09-01 10:16:35

标签: c pointers recursion

我正在查看下面的代码,我使用递归在C中以相反的顺序打印字符串:

void ReversePrint(char *str) { //line 1
  if(*str) {                   //line 2
      ReversePrint(str+1);     //line 3
      putchar(*str);           //line 4
  }
}

我对C比较陌生并且被第2行弄糊涂了。*str从我的理解是取消引用指针并应该返回当前位置的字符串值。但是如何将它用作条件语句的参数(除了布尔权限?)?在第3行,指针将始终递增到下一个块(自int为4个字节)...因此,如果在字符串结束后的下一个内存块中发生数据,则此代码无法失败?

更新:所以c中没有布尔类型正确吗?如果值为0,则条件语句的计算结果为“false”,否则为“true”?

10 个答案:

答案 0 :(得分:38)

第2行检查当前字符是否为字符串的空终止符 - 由于C字符串以空值终止,并且空字符被视为false值,因此当它到达结尾时它将开始展开递归字符串(而不是尝试在空终止符之后调用字符上的StrReverse4,这将超出有效数据的范围)。

另请注意,指针指向char,因此递增指针仅增加1个字节(因为char是单字节类型)。

示例:

 0  1  2  3
+--+--+--+--+
|f |o |o |\0|
+--+--+--+--+
  1. str = 0时,*str'f',因此对str + 1 = 1进行递归调用。
  2. str = 1时,*str'o',因此会对str + 1 = 2进行递归调用。
  3. str = 2时,*str'o',因此会对str + 1 = 3进行递归调用。
  4. str = 3时,*str'\0'\0为假值,因此if(*str)的计算结果为false,因此不进行递归调用,从而返回我们得到的递归...
  5. 最近的递归之后是`putchar('o'),之后是
  6. 接下来最近的递归之后是`putchar('o'),之后是
  7. 最近的递归之后是`putchar('f'),我们已经完成了。

答案 1 :(得分:3)

C字符串的类型只是指向char的指针。惯例是指针指向的是一个字符数组,以零字节终止。

因此,

*strstr指向的字符串的第一个字符。

如果*str指向(空)字符串中的终止空字节,则在条件中使用false求值为str

答案 2 :(得分:2)

在字符串的末尾通常是一个0字节 - 行if (*str)正在检查该字节是否存在,并在它到达时停止。

答案 3 :(得分:1)

在字符串的末尾有一个0 - 所以你有"test" => [0]'t' [1]'e' [2]'s' [3]'t' [4]0

if(0) -> false

这样就行了。

答案 4 :(得分:1)

  

在第3行中,指针将始终递增到下一个块(自int以来为4个字节)...

这是错的,这是char *,它只会增加1.因为char只有1个字节长。

  

但是如何将它用作条件语句的参数(除了布尔值右边?)?

你可以使用if($$)at $$的任何值,它只会检查它是否为非零,基本上bool也实现为simple 1 = true和0 = false。

在其他更高级别的强类型语言中,你不能在if中使用这些东西,但在C中,一切都归结为数字。你可以使用任何东西。

if(1) // evaluates to true 
if("string")  // evaluates to true
if(0) // evaulates to false

你可以在C中的条件中给出任何内容。

答案 5 :(得分:0)

条件语句(ifforwhile等)期望一个布尔表达式。如果您提供整数值,则评估结果为0 == falsenon-0 == true。如上所述,c字符串的终止字符是空字节(整数值0)。因此if将在字符串的末尾(或字符串中的第一个空字节)失败。

顺便说一句,如果你对NULL指针执行*str,则调用未定义的行为;在解除引用之前,应始终验证指针是否有效。

答案 6 :(得分:0)

1

str是指向char的指针。递增str将使指针指向字符串的第二个字符(因为它是一个char数组)。 注意:递增指针将按指针指向的数据类型递增。

例如:

int *p_int;
p_int++;     /* Increments by 4 */

double *p_dbl;
p_dbl++;      /* Increments by 8 */

2

if(expression)
{
   statements;
}

计算表达式,如果结果值为零(NULL\00),则不执行语句。由于每个字符串以\0结尾,递归将不得不结束一段时间。

答案 7 :(得分:0)

C没有布尔值的概念:在C中,每个标量类型(即算术和指针类型)都可以在布尔上下文中使用,其中0表示false且非零{{1} }。

由于字符串以空值终止,终结符将被解释为true,而其他每个字符(具有非零值!)将为false。这意味着有一种简单的方法来迭代字符串的字符:

true

for(;*str; ++str) { /* so something with *str */ } 做同样的事情,但通过递归而不是迭代。

答案 8 :(得分:0)

尝试使用此代码,这与您正在使用的代码一样简单:

int rev(int lower,int upper,char*string)
{
  if(lower>upper)
          return 0;
   else
          return rev(lower-1,upper-1,string);
}

答案 9 :(得分:0)

这是一个偏离主题,但当我看到这个问题时,我立刻想知道这是否真的比仅仅做一个strlen并从后面迭代更快。

所以,我做了一点测试。

#include <string.h>

void reverse1(const char* str)
{
    int total = 0;
    if (*str) {
            reverse1(str+1);
            total += *str;
    }
}

void reverse2(const char* str)
{
    int total = 0;
    size_t t = strlen(str);
    while (t > 0) {
            total += str[--t];
    }
}

int main()
{
    const char* str = "here I put a very long string ...";

    int i=99999;

    while (--i > 0) reverseX(str);
}

我首先使用X = 1(使用函数reverse1)编译它,然后使用X = 2编译它。两次都是-O0。

递归版本一直返回约6秒,strlen版本返回1.8秒。

我认为这是因为strlen是在汇编程序中实现的,递归会增加很多开销。

我很确定基准是有代表性的,如果我弄错了,请纠正我。

无论如何,我想我应该和你分享。