【问题标题】:C++: Print only one charC++:只打印一个字符
【发布时间】:2010-06-02 13:03:08
【问题描述】:

当我从std::cin 中读取一个char* 并想将其写入std::cout 时,它会打印直到在内存中找到\0。所以做了什么:

char c;
cin >> c;
char* pChar = &c;
pChar++;
*pChar = '\0';
println(&c); // My own method: void println(char * str) { cout << str << endl; }

但我认为这不是一个安全的操作。

有没有更安全的方法来做到这一点?

【问题讨论】:

  • 我想在回答之前先了解您想要达到的目标。如果您只是想从 cin 复制到 cout,您的代码看起来太复杂了。
  • 您的代码是未定义行为。您正在堆栈中创建一个变量,然后创建一个指向它的指针,最后覆盖保留空间之外的地址。你的代码不仅不安全,而且是错误的。

标签: c++ pointers char iostream


【解决方案1】:

简单地说:

char c;
cin >> c;
cout << c;

【讨论】:

  • 不按照 OP 的要求去做。
  • 所以问题是我将它作为char* 传递,cout
  • @Matijn:“原始发帖”或“原始海报”——我认为这个词来自 Usenet。
  • @Neil:为什么你认为这不是 OP 所要求的?
  • @Neil:Martijn 写道“我从 std::cin 读取了一个 char*,我想将其写入 std::cout”,只有在他的代码中有一个 println() 函数,我(显然还有其他人)把它当作他努力让它发挥作用的产物。
【解决方案2】:

已经有一些关于如何做你想做的事情的答案,但我想解释一下你的代码中pchar++ 下的实际内容以及为什么它是邪恶的。

C 风格 铸造赋予了灵活性,但允许你在脚下开枪。您已经在堆栈上分配了char c(这意味着您为它保留了确切的内存量,它等于sizeof(char),也就是1 字节)。

然后您获取变量的内存地址(没关系),这样您就有了 char 变量和一个 指针 指向此 char 所在的内存位于。但是您的下一步完全是邪恶的,因为您(在执行 *(pchar + 1) = 0 时)实际上正在破坏堆栈。

正如我之前所说,编译器为您的 char 内存保留了一个字节,但您所做的意味着显式遍历该内存中的下一个字节(甚至可能属于其他程序的堆栈) 并用你自己的价值观重写它。

这个行为也可以用下面的例子来描述:

认为您必须更改硬盘上某些具体文件 (a.file) 的内容。你觉得重写a.file后面的文件内容可以吗,比如增加那个a.file的大小?

【讨论】:

  • 理论上,至少据我所知,大多数现代操作系统都会阻止进程写入它不拥有的内存(当然,除非它是明确的共享成员)。虽然我同意它会破坏堆栈这一事实,但在大多数现代操作系统中它不会因为操作系统会阻塞进程。
  • 谢谢,我就是这么想的。这是否会导致内存泄漏。
  • 尝试理解内存泄漏是什么意思。我知道,将自己从 Java 移植到 C 或 C++ 时可能会很困难,但你应该知道它代表什么。
  • @Nathan 可能,是的,但我认为这不应该原谅那些做坏事的人。
  • @Nathan:进程拥有堆栈变量后面的内存的可能性非常非常,几乎可以肯定。
【解决方案3】:

当你想打印一个字符时,正确的做法当然是打印那个字符:

char ch;
if(std::cin >>ch) 
  std::cout << ch;
else
  // handle input error

如果您出于某种原因想要在每个字符后面添加一个换行符,那么就这样做:

std::cout << char << '\n';

注意:您还会看到std::endl 用于输出换行符,但std::endl 做了两件事:它输出'\n'刷新输出缓冲区。虽然后者在输出到控制台时不是问题(它仍然比人类能够读取的要快),但我看到应用程序变得很慢(在文件输出上)由于使用 std::endl 而不是'\n'。因此,您最好从一开始就注意是否需要刷新这些缓冲区。


注意,你在哪里做

pChar++;
*pChar = '\0';

您正在调用可怕的未定义行为:使用pChar++,您正在递增指向变量后面的指针(这很好),而使用*pChar = ...,您正在写入任何内存恰好在您的变量后面。
在实践中,这可能会破坏你的堆栈,希望在下一刻让你的应用程序崩溃。然而,理论上,这会调用未定义的行为,根据 C++ 标准,它可能会做任何事情(包括经常提到的可能格式化磁盘的可能性)。
不要写入内存你不拥有。
初学者很难获得正确的指示,因此请尽可能避免使用它们。您可以使用 C++ 做很多事情,而无需手动摆弄指针。

【讨论】:

  • 无论如何都应该初始化 POD:P
  • @Nikko:不,为什么?它的值将在下一个语句中被覆盖(如果输入成功)。在此之前将其设置为某个值有什么好处?
  • 如果 std::cin 失败,它的值不会是 :)
  • @Nikko:这就是为什么如果输入失败,它的值不会被访问(在这种情况下:打印)。
【解决方案4】:
char c;
cin >> c;
char pChar[2] = {0};
pChar[0] = c;;
println(pChar); 

或者如果你想更像 C++,重载函数:

void println( const char * s ) {
   cout << s << endl;
}

void println( char  c ) {
   cout << c << endl;
}

甚至是模板:

template <typename T >
void println( T t ) {
   cout << t << endl;
}

【讨论】:

  • 您不能将char[] 传递给char*
  • @Martijn:你为什么这么说?
【解决方案5】:

打印您输入的字符:

char c = 0;
std::cin >> c;
std::cout << c;

获取字符串中的字符并获取第一个字符:

char c = 0;
std::cin >> c;
std::string str;
str += c;

std::cout << str.at(0) << std::endl;

编辑

或者也许你要求这个? :

std::string str;
std::cin >> str;

std::cout << str;

我不确定真正的问题是什么。

【讨论】:

  • &lt;sigh&gt; 如果输入失败,您的代码会打印一个随机字符。 (这让你的同事非常恼火,恰好是0x7。)
  • 好的,我忘了初始化字符 :) 如果 std::cin 失败,它不会改变输入。
  • 现在,如果输入失败,它会默默地不打印任何内容。很抱歉,但我并不认为这是一种改进。
  • 因为这只是一个简单的例子?如果我不初始化 char 以及为什么对于具有构造函数的 std::string 不需要它,这并不是真正的重点……我认为 OP 是初学者……
【解决方案6】:
char c;
std::cin >> c;
if (std::cin) {
    std::cout << c << std::flush;
}

该代码打印“只有一个字符”。 std::cout &lt;&lt; c &lt;&lt; std::endl 打印两个:c'\n'

如果需要,您也可以使用 println 函数:

char str[2] = {'\0', '\0'};
std::cin >> str[0];
if (std::cin) {
    println(str);
}

注意if (std::cin) 导致编译器调用std::ios 类的operator void*() const 成员,std::istream 扩展,以便将std::cin 评估为布尔值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 2021-11-01
    • 2013-02-23
    相关资源
    最近更新 更多