简体   繁体   English

这个C代码如何工作?

[英]How does this C code work?

I was looking at the following code I came across for printing a string in reverse order in C using recursion: 我正在查看以下代码,我使用递归在C中以相反的顺序打印字符串:

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

I am relatively new to C and am confused by line 2. *str from my understanding is dereferencing the pointer and should return the value of the string in the current position. 我对C比较新,我对第2行感到困惑。 *str我的理解是*str取消引用指针,应该返回当前位置的字符串值。 But how is this being used as an argument to a conditional statement (which should except a boolean right?)? 但是如何将它用作条件语句的参数(除了布尔权限?)? In line 3, the pointer will always be incremented to the next block (4 bytes since its an int)...so couldn't this code fail if there happens to be data in the next memory block after the end of the string? 在第3行,指针将始终递增到下一个块(自int为4个字节)...因此,如果在字符串结束后的下一个内存块中发生数据,则此代码无法失败?

Update : so there are no boolean types in c correct? 更新 :所以c中没有布尔类型正确吗? A conditional statement evaluates to 'false' if the value is 0, and 'true' otherwise? 如果值为0,则条件语句的计算结果为“false”,否则为“true”?

Line 2 is checking to see if the current character is the null terminator of the string - since C strings are null-terminated, and the null character is considered a false value, it will begin unrolling the recursion when it hits the end of the string (instead of trying to call StrReverse4 on the character after the null terminator, which would be beyond the bounds of the valid data). 第2行检查当前字符是否为字符串的空终止符 - 由于C字符串以空值终止,并且空字符被视为false值,因此当它到达字符串末尾时它将开始展开递归(而不是尝试在空终止符后面的字符上调用StrReverse4,这将超出有效数据的范围)。

Also note that the pointer is to a char , thus incrementing the pointer only increments by 1 byte (since char is a single-byte type). 另请注意,指针指向char ,因此递增指针仅增加1个字节(因为char是单字节类型)。

Example: 例:

 0  1  2  3
+--+--+--+--+
|f |o |o |\0|
+--+--+--+--+
  1. When str = 0 , then *str is 'f' so the recursive call is made for str+1 = 1. str = 0*str'f'因此对str + 1 = 1进行递归调用。
  2. When str = 1 , then *str is 'o' so the recursive call is made for str+1 = 2. str = 1*str'o'因此对str + 1 = 2进行递归调用。
  3. When str = 2 , then *str is 'o' so the recursive call is made for str+1 = 3. str = 2 ,则*str'o'因此对str + 1 = 3进行递归调用。
  4. When str = 3 , then *str is '\\0' and \\0 is a false value thus if(*str) evaluates to false, so no recursive call is made, thus going back up the recursion we get... str = 3 ,则*str'\\0'\\0为假值,因此if(*str)求值为false,则不进行递归调用,因此返回我们得到的递归...
  5. Most recent recursion was followed by `putchar('o'), then after that, 最近的递归之后是`putchar('o'),之后,
  6. Next most recent recursion was followed by `putchar('o'), then after that, 接下来最近的递归之后是`putchar('o'),之后,
  7. Least recent recursion was followed by `putchar('f'), and we're done. 最近的递归之后是`putchar('f'),我们已经完成了。

The type of a C string is nothing but a pointer to char. C字符串的类型只是指向char的指针。 The convention is that what the pointer points to is an array of characters, terminated by a zero byte . 惯例是指针指向的是一个字符数组, 以零字节结束

*str , thus, is the first character of the string pointed to by str . 因此, *strstr指向的字符串的第一个字符。

Using *str in a conditional evaluates to false if str points to the terminating null byte in the (empty) string. 如果str指向(空)字符串中的终止空字节,则在条件中使用*str计算为false

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

At the end of the string there is a 0 - so you have "test" => [0]'t' [1]'e' [2]'s' [3]'t' [4]0 在字符串的末尾有一个0 - 所以你有"test" => [0]'t' [1]'e' [2]'s' [3]'t' [4]0

and if(0) -> false if(0) -> false

this way this will work. 这样就行了。

In line 3, the pointer will always be incremented to the next block (4 bytes since its an int)... 在第3行中,指针将始终递增到下一个块(自int以来为4个字节)...

Thats wrong, this is char *, it will only be incremented by 1. Because char is 1 byte long only. 这是错误的,这是char *,它只会增加1.因为char只有1个字节长。

But how is this being used as an argument to a conditional statement (which should except a boolean right?)? 但是如何将它用作条件语句的参数(除了布尔权限?)?

You can use any value in if( $$ ) at $$, and it will only check if its non zero or not, basically bool is also implemented as simple 1=true and 0=false only. 你可以使用if($$)at $$中的任何值,它只检查它是否为非零,基本上bool也实现为simple 1 = true和0 = false。

In other higher level strongly typed language you cant use such things in if, but in C everything boils down to numbers. 在其他更高级别的强类型语言中,你不能在if中使用这些东西,但在C中,一切都归结为数字。 And you can use anything. 你可以使用任何东西。

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

You can give any thing in if,while conditions in C. 在C条件下,你可以给出任何东西。

conditional statements ( if , for , while , etc) expect a boolean expression. 条件语句( ifforwhile等)期望一个布尔表达式。 If you provide an integer value the evaluation boils down to 0 == false or non-0 == true . 如果提供整数值,则评估结果为0 == false non-0 == true As mentioned already, the terminating character of a c-string is a null byte (integer value 0). 如上所述,c字符串的终止字符是空字节(整数值0)。 So the if will fail at the end of the string (or first null byte within the string). 因此if将在字符串的末尾(或字符串中的第一个空字节)失败。

As an aside, if you do *str on a NULL pointer you are invoking undefined behavior; *str一句,如果你对NULL指针执行*str ,则调用未定义的行为; you should always verify that a pointer is valid before dereferencing. 在解除引用之前,应始终验证指针是否有效。

1. 1。

str is a pointer to a char. str是指向char的指针。 Incrementing str will make the pointer point to the second character of the string (as it's a char array). 增加str将使指针指向字符串的第二个字符(因为它是一个char数组)。 NOTE: Incrementing pointers will increment by the data type the pointer points to. 注意:递增指针将按指针指向的数据类型递增。

For ex: 例如:

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

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

2. 2。

if(expression)
{
   statements;
}

The expression is evaluated and if the resulting value is zero ( NULL , \\0 , 0 ), the statements are not executed. 计算表达式,如果结果值为零( NULL\\0 0 ),则不执行语句。 As every string ends with \\0 the recursion will have to end some time. 由于每个字符串以\\0结尾,递归将不得不结束一段时间。

C has no concept of boolean values: in C, every scalar type (ie arithmetic and pointer types) can be used in boolean contexts where 0 means false and non-zero true . C没有布尔值的概念:在C中,每个标量类型(即算术和指针类型)都可以在布尔上下文中使用,其中0表示false ,非零表示true

As strings are null-terminated, the terminator will be interpreted as false , whereas every other character (with non-zero value!) will be true . 由于字符串以空值终止,终结符将被解释为false ,而其他每个字符(具有非零值!)都将为true This means means there's an easy way to iterate over the characters of a string: 这意味着有一种简单的方法来迭代字符串的字符:

for(;*str; ++str) { /* so something with *str */ }

StrReverse4() does the same thing, but by recursion instead of iteration. StrReverse4()做同样的事情,但通过递归而不是迭代。

Try this code, which is as simple as the one which you are using: 试试这个代码,就像你正在使用的代码一样简单:

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

This is kind of off topic, but when I saw the question I immediately wondered if that was actually faster than just doing an strlen and iterate from the back. 这是一个偏离主题,但当我看到这个问题时,我立即想知道这是否真的比仅仅做一个strlen并从后面迭代更快。

So, I made a little test. 所以,我做了一点测试。

#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);
}

I first compiled it with X=1 (using the function reverse1) and then with X=2. 我首先使用X = 1(使用函数reverse1)然后使用X = 2编译它。 Both times with -O0. 两次都是-O0。

It consistently returned approximately 6 seconds for the recursive version and 1.8 seconds for the strlen version. 对于递归版本,它一直返回大约6秒,对于strlen版本,它返回1.8秒。

I think it's because strlen is implemented in assembler and the recursion adds quite an overhead. 我认为这是因为strlen是在汇编程序中实现的,并且递归会增加相当大的开销。

I'm quite sure that the benchmark is representative, if I'm mistaken please correct me. 我很确定基准是有代表性的,如果我弄错了,请纠正我。

Anyway, I thought I should share this with you. 无论如何,我想我应该和你分享这个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM