【问题标题】:Reversing a '\0' terminated C string in place?反转'\ 0'终止的C字符串?
【发布时间】:2014-02-01 23:38:54
【问题描述】:

我有一些关于反转以空结尾的 C 字符串的概念性问题,以及关于指针性质的澄清问题。

输入可能是

char arr[] = "opal";

和代码:

void reverse(char *str) {  /* does *str = opal or does *str = o since the pointer str is type char? */

    char* end  = str; /* what is the difference between char* end and char *end? and is *end pointing to opal now? */

    char tmp;

    if (str) {  /* if str isn't null? */
        while (*end) 
            ++end; 
    }
    --end; /* end pointer points to l now */

    while (str < end) {     /* why not *str < *end? is this asking while o < l? */
        tmp = *str; /* tmp = o */

        *str++ = *end; /* what is the difference between *str++ and ++str? does *str++ = l? */
        *end-- = tmp; /* *end points to o */
        }
    }
}

【问题讨论】:

  • 如果您确实传递了"opal",那将是未定义的行为,因为您不允许修改字符串文字。
  • @ShafikYaghmour 那你会通过什么?
  • 一个字符数组,char arr[] = "opal";.
  • 要理解 C 指针,我永远不会将 char* 视为字符串,我会认为它只是一个内存地址。但在大多数情况下,char* 指针将指向包含以 NULL 结尾的字符串的内存地址。
  • 测试if (str) { 不提供安全性。如果strNULL,那么endNULL and the next LOC is --end` --> 段错误。最好if (str == NULL) return;

标签: c pointers reverse dereference


【解决方案1】:

很多问题...试图捕捉每个问题的答案:

/* *str = opal 还是 *str = o 因为指针 str 是 char 类型? */

*str'o',因为它指向第一个字符

/* char* end 和 char *end 有什么区别? *end 现在是否指向蛋白石? */

char *endchar* end 之间没有区别。当你写的时候会变得更棘手

char* a, b;

因为这相当于

char *a, b;

并不是你想的那样

char *a, *b;

这就是写char *end;更简洁的原因。

end 现在指向opal - *end'o'

if (str) { /* 如果 str 不为空? */

是的 - 测试您没有通过 NULL 指针

要测试你没有通过长度为 0 的字符串,你必须测试 *str(在测试 str 不是 NULL 之后,否则你会得到一个分段错误“敢于看 *NULL ")

while (str

测试指针 - 一个正在向末端移动,另一个正在向后移动。当您在中间相遇时,您会停下来;否则你交换两次,不会有任何净效应......

 *str++ = *end; /* what is the difference between *str++ and ++str? does *str++ = l? */

首先将*end 的值复制到*str,然后递增str 指针。如果你输入++str,你先递增,然后使用它。这意味着您将l 代替p 而不是代替o

编辑对您的代码的一个批评(超越您提出的问题,并回复@chux 的评论):当您测试if(str){} 时,您确实需要一个else return; 声明,因为你实际上做了end--;,然后使用*end。很确定0xFFFFFFFFFFFFFFFF 几乎总是一个无效地址...

如果您实际上是在测试 if(*str!='\0'),那么您仍然应该返回(空字符串是“不可逆的” - 或者更确切地说,它不需要任何东西被认为是反转的)。

顺便说一句,我更喜欢明确条件(就像我刚才所做的那样);它不仅更清楚地显示了您的意图,而且如果您执行if(str!='\0')if(*str != NULL),编译器实际上可能会抱怨,因为您正在比较的类型不兼容。这意味着您将拥有更健壮、更具可读性且更有可能实现您预期的代码。

【讨论】:

  • 如果可以的话,我会再次 +1 你为 cmets 关于最后一段中的显式测试。
  • @JonathanLeffler - 我很可能在去年的某个时候通过阅读您的一个答案/ cmets 了解了这种方法的重要性。是的,我主要是来这里学习的,尽管我是通过尝试回答问题来做到这一点的。谢谢夸奖!
【解决方案2】:

假设reverse 是这样调用的:

char str[] = "opal";
reverse(str);

并且被分配到内存中,例如,像这样:

内存地址:100 101 102 103 104 内存值:['o'] ['p'] ['a'] ['l'] ['\0']

以下关于地址的说法是正确的:

  1. str + 0 == &amp;str[0] == 100
  2. str + 1 == &amp;str[1] == 101
  3. str + 2 == &amp;str[2] == 102
  4. str + 3 == &amp;str[3] == 103
  5. str + 4 == &amp;str[4] == 104

以下关于价值观的说法是正确的:

  1. *(str + 0) == str[0] == 'o'
  2. *(str + 1) == str[1] == 'p'
  3. *(str + 2) == str[2] == 'a'
  4. *(str + 3) == str[3] == 'l'
  5. *(str + 4) == str[4] == '\0'

关于NULL,当一个指针未初始化,即未指向有效内存时,应为其赋值NULL,大多数情况下为地址0。考虑如下:

char *str = NULL;
reverse(str);

当计算以下表达式时:

if(str)

它将评估为FALSE。在reverse 内部,它应该(你应该修复它)立即return,因为使用地址为NULL 的指针会导致不确定的行为,例如分段错误。

str的地址为NULL时,将str的地址赋值给end,然后递减:

--end;

将导致不确定的行为。

【讨论】:

  • 这并没有涵盖问题中的所有问题。
  • @JonathanLeffler 你是对的,但如果你能理解这一点,其他问题的答案就会变得显而易见。
  • @Awwww。不,但同意乔纳森莱弗勒:D
  • @haacks 我扩展了我的答案。但你是对的。问题中有足够的问题可以写一整章来涵盖所有内容。 :-D
【解决方案3】:

*str = opal 还是 *str = o 因为指针 str 是 char 类型?

str是指向opal的第一个元素的指针,即指向第一个字母o的指针。所以,*s 表示,您正在取消引用 s(第一个地址),因此它是 'o'

char* end 和 char *end 有什么区别? *end 现在是否指向蛋白石?

没有区别。不,是end 指向opal,即它现在的第一个元素。

如果 str 不为空?

是的。

结束指针现在指向 l

是的。它现在指向单词的最后一个字母。

*str++ 和 ++str 有什么区别? *str++ = l 吗?

*str++ 表示str 将在取消引用str 指向的值之后递增。 ++str 只是 str 的预递增。
*str++ = 在递增 str 之前将值分配给取消引用的变量。

【讨论】:

  • 我没有投反对票,肯定是别人投的。我刚刚编辑了问题以通过char arr[] = "opal";
  • 好答案教授黑客。 +1
【解决方案4】:

*str = opal 还是*str = 'o',因为指针strchar 类型?

两者兼而有之。我知道这很令人困惑,但这就是正在发生的事情:在 C 中,指针可以用两种方式解释 - 字面意思,即作为指向单个项目的指针(在这种情况下为 char)或作为指向开头的指针一系列相同类型的项目。在后一种情况下,将定义序列的结尾。有三种方法可以知道序列在哪里结束 - 通过明确知道长度,通过知道结束指针,或者通过在序列末尾放置一个“终止符”项。对于 C 字符串,使用称为“空终止符”的特殊字符 '\0' 来终止序列。

char* endchar *end 有什么区别? *end 现在指向蛋白石了吗?

绝对没有区别——它们都指向同一个地方。星号的位置无关紧要。

如果str 不是null

正确

end 指针现在指向'l'

绝对!

为什么不*str &lt; *end?这是在问while o &lt; l吗?

您正在比较内存中的位置,而不是它们指向的字符。这是要求指针不要相互“交叉”,因为两者都是从字符串的两端向中心移动的。

tmp = o

在第一次迭代中,它是。在第二次迭代中,它指向'p'

*str++++str 有什么区别? *str++ = 'l'

++str表达式将指针的值递增,递增后计算为指针的值; *str++ 表达式将指针的值递增,并计算出指针在递增之前 所指向的字符 的值。

【讨论】:

    【解决方案5】:

    *str = opal 还是 *str = o 因为指针 str 是 char 类型?

    *str 取消引用类型为char* 的指针,因此您得到的类型为charchar 将是str 指向的值,即'o'

    char* end 和 char *end 有什么区别?

    什么都没有。

    现在 *end 是否指向蛋白石?

    是的,好吧,关闭。 end 指向与str 完全相同的地址,这是字符串的开头。 *end 是字符类型,而不是指针。其值为'o'

    如果 str 不为空?

    正确,对指针进行布尔测试是标准测试。如果指针不为空,则它评估为“真”值,否则为“假”。请注意,这与零值不同。 C 标准允许任何值表示空地址。

    为什么不 *str

    不,它是在比较实际的内存地址。它说循环,而str 指向字符串的早于end 的部分。您会注意到,在循环期间,str 增加,end 减少。所以最终他们会互相擦肩而过或在同一个字符处相遇(字符串的中间)。

    *str++ 和 ++str 有什么区别? *str++ = l 吗?

    str++ 首先应用,它递增str 并返回其先前的值,然后* 一元运算符将在该旧位置给出字符。是的,第一次,来自end'l' 将被分配到字符串的开头(其中str 曾经在它递增之前指向)。 tmp 用于携带旧字符并将其分配给end。这是标准的“交换”操作。

    【讨论】:

      猜你喜欢
      • 2014-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-04
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多