【问题标题】:String reverse in CC中的字符串反转
【发布时间】:2013-10-12 09:21:51
【问题描述】:

我有反转字符串的代码。假设我输入“ABC”,输出将是“CBA”。但是,有些代码行我很不明白。

1    #include <stdio.h>
2    #include <string.h>
3
4    void print_reverse(char *s) {
5   size_t len = strlen(s);
6
7   char *t = s + len-1;
8   while(t >= s) {
9       printf("%c", *t);
10      t = t-1;
11  }
12  puts("");
13  }
14
15    int main()
16    {
17  char charinput[100];
18  printf("Enter character you want to reverse:");
19  fgets(charinput, 100, stdin);
20  print_reverse(charinput);
21  getchar();
22    }

第 7 行和第 8 行是做什么的?指针 t 的输出是什么?

【问题讨论】:

    标签: c string reverse


    【解决方案1】:

    发布的代码使用以下算法:

    • 第 7 行:将指针 t 设置为字符串中的最后一个字符(注意:如果用户输入的字符串少于 99 个字符,它将是换行符)。 -1 是从终止的 nil-char 中移回一个字符
    • 第 8-10 行:这是反转报告循环的核心。指针t 针对字符串开头的地址反复测试。条件子句检查t 值(地址)是否大于或等于字符串的起始地址。只要它是,就输入循环体,并且当前位于t 中保存的地址的字符通过printf() 发送到标准输出。然后t 中的地址减少一个类型宽度(在大多数系统上为一个字节,具有单字节char)并重复循环。只有当t 包含一个地址before s 时,循环才会中断(注意:这不在标准范围内;原因见下文)。

    你应该知道关于这个循环的一些事情(如果不是你,应该向作者指出)。最终的指针比较符合标准。该标准规定了非空、类似类型的指针之间的比较是有效的,从有效序列的基地址(此代码中的charinput,通过s 参数化的地址)直到并包括一个类型元素 过去分配的内存区域。此代码将ts 进行比较,仅当t 为“更少”时才打破循环。但是一旦t 小于s,它的值就不再与s 在法律上具有可比性。根据标准,这是因为t 不再包含从charinputcharinput 内存块大小的1 倍的有效地址。

    正确执行此操作的一种方法如下:

    罢工>

    t = s + len;
    while (t-- > s)
        printf("%c", *t);
    

    编辑:在Paul Hankin 的推动下进入标准之旅后,先前的代码已被重写以解决未被注意到的 UB 条件。更新后的代码记录如下:

    t = s + len;
    while (t != s)
        printf("%c", *--t);
    

    这也适用于零长度字符串。其工作原理如下:

    • t 设置为字符串的终止 nulchar 的地址。
    • 进入循环,只要t中的地址不等于s的基地址,条件就继续。
      • 递减t,然后取消引用结果地址以获取当前字符,将结果发送到printf
      • 循环进行下一次迭代。

    【讨论】:

    • @hacks 好的。好的。你的分析是准确的,这是我唯一想说的(如果你和我一样,发现这一点后你开始想的第一件事是“哎呀,我过去违反该规则的频率是多少?”=)
    • 只是在字符串开头递减 t 已经是未定义的行为,没有比较。
    • 标准中的 6.5.6.8。在添加指针和整数时描述结果:“如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为未定义。”
    • 而 6.5.2.4.2-3 描述了 p-- 与从 p 中减去 1 具有相同的效果(并参考 6.5.6 进行讨论、效果和约束)。
    • 在该段落中,“结果”是将整数添加到指针的表达式的结果。该标准不区分结果的存储位置(或根本不存储)。
    【解决方案2】:

    让我们一步一步来理解它:

    1. len = strlen(s) 将指向len 的字符串s 的大小分配给len(假设len 为10)。

    2. s 指向字符串的第一个字符。假设这个字符串的第一个元素的地址是100,那么s包含100

    3. len-1 添加到s 将得到109

    现在,第 7 行

       char *t = s + len-1;
    

    告诉编译器t 指向地址109 的元素,即字符串的最后一个元素。

    第 8 行

       while(t >= s) {
    

    告诉编译器循环会一直持续到t指向之前的东西字符串的第一个元素。

    【讨论】:

    • 与 Matthias 的回答类似,此描述中的中断条件不准确。 while(t &gt;= s) 告诉编译器循环,直到 t 指向 before s;直到它指向第一个元素。这很重要,因为它使循环不符合标准。
    • @WhozCraig;我不明白你在说什么? (对不起。请详细说明)。
    • 见我回答的第二部分。它几乎说明了这一点。
    • @hacks 我认为@WhozCraig 的观点是指针t 应该只假定在&amp;s[0]&amp;s[strlen(s)+1+1] 之间有效。该例程尝试将t 设为&amp;s[-1]。尽管这可能在许多情况下都有效,但您创建的指针不能保证比较小于sts 好的已知范围之前。 C11dr 6.5.8 6“...指向同一个数组对象的元素,或数组对象的最后一个元素,求值不应产生溢出;否则,行为未定义”
    • @hacks 假设操作系统将仅使用 32 位来提高速度效率。它可能是 32 位处理器,但具有 48 位地址空间。其他示例:segment:offset、Page table 和en.wikipedia.org/wiki/Physical_address
    【解决方案3】:

    第 7 行:指针 t 指向最后一个字符 (s+len-1)
    第8行:当t的地址等于或大于s的地址时重复该步骤。假设如果 s 指向第一个输入字符串的地址是 1101,则下一个字符的地址是 1101+1=1102,第三个是 1102+1=1103,以此类推。因此,如果您输入的长度为 10 个字符,则第 7 行中指向 1101 + len-1 的 t 将是 1101+10-1 (1110)
    第 9 行:打印由t 指向的地址所持有的字符。 第 10 行:t 减 1,现在指向最左边的字符。
    9 和 10 在地址大于或等于时重复(在我的插图中为 1110)

    【讨论】:

      【解决方案4】:

      t 开始指向字符串 s 的最后一个字符,并在接下来的循环中递减,直到它指向第一个字符。对于每个循环迭代,都会打印字符。

      【讨论】:

      • 您可能需要重新考虑此答案的“直到”子句。指向第一个字符并不是打破循环的原因。而且它确实采用的机制不符合标准。
      【解决方案5】:

      第 7 行将指针 t 设置为指向字符串 s 的末尾。第 8 行是一个 while 循环(它将向后遍历字符串,直到开始)。指针 t 是字符串中的当前位置,在第 9 行输出。

      【讨论】:

        【解决方案6】:

        char *t = s + len-1;:指向字符串s的最后一个字符 while(t &gt;= s) :逆序扫描字符串 s 中的所有字符(s 指向第一个字符,而t 指向line 7 中的最后一个字符)。

        希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-07
          • 1970-01-01
          • 1970-01-01
          • 2010-10-21
          • 2015-05-24
          相关资源
          最近更新 更多