【问题标题】:while loop inside for loopfor循环内的while循环
【发布时间】:2018-11-05 20:04:49
【问题描述】:

我在一本书中阅读了这个示例代码。我不明白为什么以下示例代码的函数声明的这一部分是必要的:

while (i <= n)
    p[i++] = '\0'; // set rest of string to '\0'

这是整个代码:

#include <iostream>

const int ArSize = 80;

char * left(const char * str, int n = 1);

int main()
{
    using namespace std;
    char sample[ArSize];

    cout << "Enter a string:\n";

    cin.get(sample,ArSize);

    char *ps = left(sample, 4);
    cout << ps << endl;

    delete [] ps; // free old string

    ps = left(sample);
    cout << ps << endl;

    delete [] ps; // free new string
    return 0;
}
// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{
    if(n < 0)
        n = 0;

    char * p = new char[n+1];
    int i;

    for (i = 0; i < n && str[i]; i++)
        p[i] = str[i]; // copy characters

    while (i <= n)
        p[i++] = '\0'; // set rest of string to '\0'

    return p;
}

我擦掉后运行代码,没有问题。

【问题讨论】:

  • 不需要循环,但如果要将p 视为以空字符结尾的字节字符串,则需要p[i++] = '\0'
  • 另外,请学习如何缩进你的代码。它很可能在书中缩进了,虽然编译器不需要缩进,但它确实有助于人们阅读代码。
  • 而且 while 循环没有嵌套在 for 循环内 - 在 for 语句的关闭之后没有立即打开大括号,因此它只循环 p[i] = str[i]; 这种混乱/缺乏清晰度是一些编码标准要求所有 for/while/if/else 语句后跟 {}
  • 你可以用p[i] = '\0';替换它,一个终止的null就足够了
  • while 循环很可笑。在for 循环之后需要p[i] = 0; 来正确终止字符串。如果输入字符串str 的长度小于n,则为p 分配的内存量也过多。这是哪本书?

标签: c++ for-loop while-loop


【解决方案1】:

循环是不必要的。以空字符结尾的字符串在第一个空字节处结束。如果分配的内存比实际字符串需要的多,那么这些额外字节中的内容无关紧要。所有未损坏的 C 字符串处理代码在第一个空终止符处停止。只需要一个

p[i] = '\0';

for 循环之后。但是,一个空字节是强制性的。 C-string 函数依赖于它,如果它丢失了,它会很高兴地溢出分配的内存。本质上,他们会(试图)继续前进,直到他们偶然发现内存中的下一个空字节。如果超过了分配的内存,它会导致未定义的行为,如果幸运的话会导致崩溃;或损坏的数据,如果你不那么幸运。

也就是说:把昨天的那本书扔掉。代码从第一行到最后一行都是一场灾难。它几乎不符合 C++ 标准。其中大部分是纯 C。即使作为 C 代码,它也是非常值得怀疑的。

  • Why to avoid using namespace std。 @vol7ron 在 cmets 中指出,主要投诉是针对 headers 中的 using namespace std。在这里,它在 .cpp 文件中的函数内部使用,从而显着降低了影响。尽管在我看来它仍然值得避免。如果您不深入了解标准库的实现,那么您就不会真正了解您拉入范围的所有符号。如果您需要它以提高可读性,则拉入特定符号(例如using std::cout;)是更好的选择。另外,我相信我并不是唯一一个期待 std:: 前缀的人。例如,std::string 是我希望看到的。 string 看起来有点不对劲。总是有一个挥之不去的疑问,它可能不是 std 库字符串,而是自定义字符串类型。因此,包含前缀也可以有利于可读性。
  • 为什么所有的C弦疼痛? std::string 已经有一段时间了……
  • 循环复制字符?严重地?这就是 std::strcpy() 的用途。
  • 到处都是原始的newdelete:容易出错,因为您必须手动跟踪新/删除对以避免内存泄漏。
  • 更糟糕的是:非对称拥有原始指针。 left() 分配并返回一个指针; 调用者有责任删除它。没有比这更容易出错的了。

……而这些只是乍一看突出的问题。

那段代码应该是什么样子:

#include <iostream>
#include <string>

std::string left(const std::string& str, std::size_t len = 1);

int main()
{
    // getline can fail. If that happens we get an empty string.
    std::string sample;
    std::getline(std::cin, sample);

    auto ps = left(sample, 4);
    std::cout << ps << '\n';

    ps = left(sample);
    std::cout << ps << '\n';

    return 0;
}

// `len` may be longer than the string. In that case a copy
// of the complete input string is returned.
std::string left(const std::string& str, std::size_t len)
{
    return str.substr(0, len);
}

【讨论】:

  • 无意冒犯,但他在 main 中设置了命名空间,这对于这个示例来说似乎已经足够了。虽然我喜欢提到要谨慎,特别是如果这是一本较旧的书,但我不认为在函数中设置命名空间是一个糟糕的决定。是的,碰撞风险是现实存在的,但代码的可读性也很重要。
  • 本书是Stephen Prata的《C++ Primer Plus》第6版。
  • @vol7ron 嗯,也许抱怨using namespace std 变得有点太自动了。我仍然认为这是值得避免的。但这是一个小案例,我应该更新答案。
猜你喜欢
  • 2019-11-10
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多