【问题标题】:How to pass a cstring as a function parameter/argument如何将 cstring 作为函数参数/参数传递
【发布时间】:2021-01-14 17:57:02
【问题描述】:

我有一个小程序可以打印出单词的每个字母的大写形式,但是当我编译它时我得到错误有符号/无符号不匹配,因为我在这个程序中将 cstring 作为普通 string 传递.如何正确传递它以便我仍然可以使用text.length()?这是我得到的错误“Tester.cpp(22,23): warning C4018: 'for (int i = 0; i < text.length(); i++)

#include <iostream>
using namespace std;

string capitalizeFirstLetter(string text);

int main() {
    char sentence[100];
    for ( ; ; )
    {
        cin.getline(sentence, 100);
        if (sentence != "0")
            capitalizeFirstLetter(sentence);
    }

    return 0;
}

string capitalizeFirstLetter(string text) {

    for (int i = 0; i < text.length(); i++)
    {
        if (i == 0)
        {
            text[i] = toupper(text[i]);
        }
        if (text[i] == ' ')
        {
            ++i;
            text[i] = toupper(text[i]);
        }
    }
    cout << text;

    return text;
}

【问题讨论】:

  • 您可以使用std::string::c_str(),但只有在这种情况下才能通过strlen()获取长度(或者您也将其作为参数传递)。为什么不将sentence 设为std::string 并在第一名使用std::getline(cin,sentence)
  • 为什么不使用 string::size_type 作为 i 而不是 int 的类型?
  • 编译器警告意味着您为i 使用了错误的类型,而不是您错误地传递了字符串。 i 应该是 size_t 或(相同的)string::size_type

标签: c++ string user-defined-functions c-strings


【解决方案1】:

由于您将 cstring 作为普通 string 传递给函数,因此未生成错误,但这是由于您尝试在语句中使用 != 运算符比较 c 样式字符串

if (sentence != "0")
    capitalizeFirstLetter(sentence);

为此尝试使用strcmp()

【讨论】:

  • 我试过了,但错误出现在for (int i = 0; i &lt; text.length(); i++)
  • 这将是一个警告,它几乎不会对输出产生任何影响,但如果你想删除它,你可以键入 case iauto
  • 这是否意味着将每个i 替换为auto?我试过了,它产生了错误。在我的课程中我还没有遇到过auto 的使用。
  • 不,我的意思是将数据类型更改为自动(在这里了解它:geeksforgeeks.org/type-inference-in-c-auto-and-decltype
  • @Arsenic 使用auto 将不起作用。即使您将auto 放在i = 0 之前,它也会将i 作为有符号整数。您需要在比较期间显式声明或使其无符号 (; (unsigned) i &lt; text.length();)。尽管所有这些方法都有效,但我会考虑将i 声明为std::string::size_type 是最佳选择。编辑:另外,auto i = 0ULL 将完成这项工作。
【解决方案2】:

您的代码有问题:

  1. if (sentence != "0") : 非法比较。如果您想中断将0 作为输入,请尝试使用strcmp(包括&lt;cstring&gt;)作为if (strcmp(sentence, "0")。 (请注意,当两个字符串相等时,strcmp 返回0。)或者干脆做if (!(sentence[0] == '0' and sentence[1] == 0))。此外,这个条件应该伴随else break;,以防止for循环永远运行。

  2. for (int i = 0; i &lt; text.length(); i++) :由于有符号和无符号类型之间的比较而产生警告。将 i 的数据类型更改为 string::size_type 以防止出现警告。

  3. &lt;string&gt;std::string)和&lt;cctype&gt;std::toupper)不包括在内。

  4. 感谢@john 指出这一点。如果字符串的最后一个字符是空格,则您的代码具有未定义的行为。在使用text[i]之前添加检查i是否仍小于text.length()

  5. 另一种错误情况是0 后面有空格。将 getline 移动到 for 的条件以解决此问题。现在不需要输入 0 来终止程序。此外,我建议为此使用while 循环而不是for

  6. 您可能还需要打印换行符来分隔句子。此外,我更喜欢使用从capitalizeFirstLetter 返回的字符串在main() 函数中打印修改后的句子。

  7. 简短(初级)代码无关紧要,但要避免养成将using namespace std; 放在您编写的每个代码顶部的习惯。 Refer this.

固定代码:

#include <cctype>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;

string capitalizeFirstLetter(string text);

int main() {
  char sentence[100];
  while (cin.getline(sentence, 100))
    cout << capitalizeFirstLetter(sentence) << '\n';
}

string capitalizeFirstLetter(string text) {
  for (string::size_type i = 0; i < text.length(); i++) {
    if (i == 0)
      text[i] = toupper(text[i]);
    if (text[i] == ' ')
      if (++i < text.length())
        text[i] = toupper(text[i]);
  }
  return text;
}

Sample Run

输入:

hello world
foo bar

输出:

Hello World
Foo Bar

我的版本(需要 C++20):

#include <cctype>
#include <iostream>
#include <string>

auto capitalizeFirstLetter(std::string text) {
  for (bool newWord = true; auto &&i : text) {
    i = newWord ? std::toupper(i) : i;
    newWord = std::isspace(i);
  }
  return text;
}

int main() {
  std::string sentence;
  while (std::getline(std::cin, sentence))
    std::cout << capitalizeFirstLetter(sentence) << std::endl;
}

Sample Run

【讨论】:

  • 如果空格是字符串中的最后一个字符,第一个版本将失败。
  • 不,我的意思是字符串末尾的空格,例如"hello "。然后,您的代码的第一个版本将尝试将空格后面的字符大写,这当然不存在。 OP的代码也有同样的问题。
  • textstd::string。我知道 C++11 保证 \0 被存储,但不是取消引用以访问 \0 仍然是一个边界错误吗?或者您现在可以访问text[i] 哪里i == text.size()
  • @john 是的,我明白了。你说得对。 \0 将被存储到最后。取消引用它是未定义的。 Ref.
【解决方案3】:

这里有几件事困扰着我。

首先,不要使用using namespace std,在这种情况下“可以”,但不要习惯它,它会引起相当大的麻烦。 见Why is “using namespace std;” considered bad practice?

接下来就是在这里使用std::string而不是cstrings,它更容易编写和阅读,并且不会产生任何可测量的性能损失或其他东西。而且这种方式更难产生错误。 所以只需使用

std::string sentence;

getline(std::cin, sentence);

为什么要在转换字符串的函数中处理输出?只需让主打印转换后的字符串。 所以你的主要可能看起来像这样:

int main() {
    std::string sentence;
    
    while(true)
    {
        getline(std::cin, sentence);
        auto capitalized = capitalizeFirstLetter(sentence);
        std::cout << capitalized;
    }

    return 0;
}

PS:您收到的“错误”是一个警告,因为您将int itext.length() 进行比较,size_t 又名unsigned intunsigned long int

【讨论】:

  • 我明白了,这是有道理的。我正在使用using namespace std,因为我是初学者,我从书中阅读了它的使用和其他命名空间,出于可读性和安全原因,非常需要显式添加前缀,但我决定不使用std 前缀因为我还没有处理命名空间。
【解决方案4】:

sentence 作为字符串传递的最简单方法是将其括在一个花括号集合中,为参数std::string text 提供直接初始化 例如......

    for ( ; ; )
    {
        std::cin.getline(sentence, 100);
        if (*sentence)
            capitalizeFirstLetter({sentence});
    }

这允许字符串sentence 用作Direct initialization 以在您的capitalizeFirstLetter() 函数中初始化std::string text

std::string capitalizeFirstLetter (std::string text) {

    for (size_t i = 0; i < text.length(); i++)
    {
        if (i == 0)
        {
            text[i] = toupper(text[i]);
        }
        if (text[i] == ' ')
        {
            ++i;
            text[i] = toupper(text[i]);
        }
    }
    std::cout << text;

    return text;
}

阅读Why is “using namespace std;” considered bad practice?后,您的完整代码将是:

#include <iostream>

std::string capitalizeFirstLetter (std::string text) {

    for (size_t i = 0; i < text.length(); i++)
    {
        if (i == 0)
        {
            text[i] = toupper(text[i]);
        }
        if (text[i] == ' ')
        {
            ++i;
            text[i] = toupper(text[i]);
        }
    }
    std::cout << text;

    return text;
}

int main (void) {
    
    char sentence[100];
    
    for ( ; ; )
    {
        std::cin.getline(sentence, 100);
        if (*sentence)
            capitalizeFirstLetter({sentence});
    }

    return 0;
}

注意:取消引用sentence提供了第一个字符,然后确认该字符不是nul-terminating字符(ASCII 0))

更好的 CapitalizeFirstLetter()

一种更简单的大写方法是包含&lt;cctype&gt;int 来保存最后读取的字符。然后逻辑简单地循环遍历每个字符,如果第一个字符是字母字符,则将其大写,否则仅当当前字符为字母字符且最后一个字符为空格时才将字母大写,例如

std::string capitalizeFirstLetter (std::string text)
{
    int last = 0
    
    for (auto& c : text)
    {
        if (isalpha(c))
        {
            if (!i || isspace (last))
                c = toupper(c);
        }
        last = c;
    }
    std::cout << text;

    return text;
}

注意:上面使用了基于范围的for循环)

任何一种方式都可以。

【讨论】:

  • 我试过你的代码,它正确地输出了新的字符串。非常感谢您的信息和更正!你介意向我介绍: 的使用吗?这是我第一次看到它被使用,我不知道它的术语。
  • Range-based for loop (since C++11) 是一种非常方便的简写方式,用于迭代任何容器的元素。
猜你喜欢
  • 2021-04-13
  • 2014-06-29
  • 1970-01-01
  • 2013-01-27
  • 2023-04-02
  • 2016-07-23
  • 2021-10-30
相关资源
最近更新 更多