0

In my Introduction to C++ classes, we were asked to write a function that returns the length of a string using pointers. The code that I wrote (see full code below) seems to work just fine, but here's the thing I don't understand.

I would think typing 'Yes' followed by Ctrl-Z ( I'm using Windows 10) in the console would stop the input. However, after pressing Ctrl-Z -> Enter the console still waits for further input. I have to start a new line after 'Yes', press Ctrl-Z and then hit Enter again to stop the input.

Why is this the case? Is there a way to stop the input after pressing just Ctrl-Z without any of the two new lines?

I read several posts on cin here, including this, this, and this, but they don't seem to answer my question.

#include "pch.h"
#include <iostream>
using namespace std;

unsigned strlen(const char *str)
{
    int count = 0;
    while (*str != '\0') { str++; count++; }
    return count;
}

int main()
{
    char str[100] = {};
    char *pchar;
    pchar = str;
    while (cin >> *pchar) pchar++;
    pchar = str;
    cout << '\n' << strlen(pchar);
    return 0;
}
Werner Henze
  • 16,404
  • 12
  • 44
  • 69
John Allison
  • 966
  • 1
  • 10
  • 30
  • try Ctrl-C. And which Operating System are you using? – Kagiso Marvin Molekwa Nov 19 '18 at 09:09
  • @marvinIsSacul, I'm using Windows 10, added to the question body. Ctrl-C throws an exception, wkernelbase.pdb not loaded. It's that I don't just want to try this instead of that. I want to understand why my method isn't working. It seems I'm just lacking the basic understanding of how input buffer and cin work. – John Allison Nov 19 '18 at 09:36
  • 4
    Not trying to put *too* fine a point to it, but your C++ introduction course appears to teach C first... which is [not a good idea](https://www.youtube.com/watch?v=YnWhqhNdYyk&t=0s). You shouldn't be doing `char *` in C++. Ever. Nor should you be doing `strlen()` by hand (a.k.a. "reinventing the wheel"). I know you probably cannot change any of that; I am just voicing my sadness that so many "Intro to C++" courses out there are still "C (with classes)". This is doing both you and C++'s reputation a disservice. – DevSolar Nov 19 '18 at 09:37
  • @DevSolar, thanks for your point. Well, they told us up front that a lot of functions are so helpful that they've been included in C++ libraries, and this is one of them. The exercise is just a means to train us in pointers. – John Allison Nov 19 '18 at 09:46
  • 4
    @JohnAllison: ...pointers, which you shouldn't have to touch *at all*, or at least not until you're doing the advanced course. You're being taught all the C stuff -- pointers, arrays, `char *` -- all of which is unnecessary / bad C++ style, and which you will have to un-learn once you get to "real" C++. Instead of showing you `` and references and having you do *real* stuff instead of reinventing the (C) wheel. I feel this is wasting your time, and making things unnecessarily difficult for you (*and* the instructor). – DevSolar Nov 19 '18 at 09:53
  • @DevSolar, btw, why is using `char *` bad practice in C++? I thought the link in your previous comment answers the question, but it's on the general point you made. Or maybe it does, but I haven't watched to the end yet. – John Allison Nov 19 '18 at 10:02
  • 2
    @JohnAllison: In C++ we have `std::string` and the corresponding functions operating on those. Just two counterquestions: What happens, in your code, if the user enters the 101st character without ending the input? What happens if, e.g. by input redirection, the user enters a null byte somewhere in his input? Also, you can't teach C strings a.k.a. `char[]` without teaching pointers as well... which IMHO shouldn't be taught in an intro to C++, at all. – DevSolar Nov 19 '18 at 10:07
  • @DevSolar his course **will** eventually get to buffer overflows, security, etc. But for now, he just needs to understand why the program isn't terminating by Ctrl-Z. 1 step at a time. – Kagiso Marvin Molekwa Nov 19 '18 at 10:11
  • 1
    @marvin: His course should have *started* with the C++ that makes buffer overflows a non-issue. And I know I am not answering. I am commenting. – DevSolar Nov 19 '18 at 10:12
  • yes, I agree with you @DevSolar – Kagiso Marvin Molekwa Nov 19 '18 at 10:26
  • @DevSolar, I fully get all of that about strings and limitations of my approach and am with you. Having said that, I still think it's helpful to learn a bit about arrays and chars just to have an understanding of the fact that a string is almost but an array of chars, it's just that those checks are built-in in the class. – John Allison Nov 19 '18 at 10:55
  • 2
    @JohnAllison: And I understand the "bottom up" approach and all that, but I disagree with its helpfulness. Students want to achieve *results*. Especially with C++ where everybody tells you how godawful *hard* it reputedly is (which it is only if you have to learn, then unlearn C first), I find it more helpful to go top-down, and dive right in using string, vector, ``, polymorphy and templates to their full potential instead of worrying about what's under the hood. That's for the advanced course, "library development"... – DevSolar Nov 19 '18 at 11:33

2 Answers2

2

If you press Ctrl-Z after typing some other text, it flushes the line buffer (it does not set end-of-file condition).

You have to press it twice in a row; or press it after a newline; to cause the end-of-file condition to occur.

Your misunderstanding is to do with the windows console behaviour, not with C++ streams per se.

See also: Why do I require multiple EOF (CTRL+Z) characters?

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
M.M
  • 138,810
  • 21
  • 208
  • 365
1

Firstly we have to understand what you while loop actually does.

while (cin >> *pchar) pchar++; says that continue getting input from the stdin whilst cin has not encountered an error (or to be specific whilst !cin.fail() == true).

Note that cin is basically an object of std::istream.

Then secondly we have to understand what causes std::istream::fail to return true. Here they are saying that std::istream::fail returns true if the badbit or failbit flags are set (and/or also if any non EOF error occurs).

Having said that, Ctrl-Z is actually EOF (end of file). And based on what I have said above, std::istream::fail will return true if any none EOF error occurs, and of course return false if an EOF occurs.

So in short, EOF/Ctrl-Z does not cause std::istream::fail to return true hence the loop will continue running.

If you really want to have your program cease executing when EOF/Ctrl-Z gets hit, then adjust your loop to something like this...

 while ((cin >> *pchar) && !cin.eof()) pchar++;

Now the loop above reads, whilst std::istream::fail() == false and no EOF/Ctrl-Z character has been entered, continue looping. Else cease the loop.

I hope that fully answers you.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
  • Thanks - it's _almost_ clear now. So turning back to my sequence that worked (Enter - Ctrl-Z - Enter), what exactly causes `cin.fail()` get to `true`? – John Allison Nov 19 '18 at 10:51
  • It did, yes. Typing, e.g., 'Yes', then Enter, Ctrl-Z, Enter stops the input and has 3 as an output, so works just fine. Moreover, I just noted that adding `!cin.eof()` in the condition doesn't help. Pressing Ctrl-Z -> Enter right after 'Yes' doesn't stop the input either. – John Allison Nov 19 '18 at 10:59
  • if `cin >> *pchar` sets `eofbit`, it will also set `failbit` due to failing to read a character, this is explained by the link you posted. The advice to add in a test of `!cin.eof()` is poor – M.M Nov 19 '18 at 11:04
  • From [here](https://en.cppreference.com/w/cpp/io/ios_base/iostate), I think its because the end of the stream (EOF) was encountered while reading the next character (Enter). I'm not quite sure. Follow that link and many others within that, to find out more – Kagiso Marvin Molekwa Nov 19 '18 at 11:16
  • But @M.M if program termination is required to be Ctrl-Z, then I don't see how `!cin.eof()` is poor, because in fact `!cin.eof()` is literally wating for Ctrl-Z to be input. This is going back to the OP by the way – Kagiso Marvin Molekwa Nov 19 '18 at 11:19
  • When `cin >> *pchar` returns, there's only 2 possibilities (it returns `false` and stored a char; or it returns `true` and didn't store a char). The latter obviously implies that failbit is set. However in practical terms nothing will ever set failbit except for EOF. The single ^Z press of OP doesn't set failbit , the loop keeps waiting for a character until there is the second ^Z which does set failbit and eofbit. – M.M Nov 19 '18 at 11:38
  • Yes I see. But the solution managed to suffice to the OP. Hence the question was successfully answered – Kagiso Marvin Molekwa Nov 19 '18 at 11:40
  • And I'm quite sure, others who are having a similar problem could also be assisted by my solution (and yours of course). – Kagiso Marvin Molekwa Nov 19 '18 at 11:41
  • @marvinIsSacul, unfortunately, I prematurely accepted your answer. Adding `!cin.eof()` didn't do the job and I still had to press Enter, then Ctrl-Z. But I guess it's still needed if I am getting input from a file. – John Allison Nov 19 '18 at 11:51
  • Remember what we said on M.M's answers. This problem seems to Operating System dependent. on Windows (cmd), 2 consecutive Ctrl-Z put an `EOF` char. And on Linux only one Ctrl-Z is needed to put an `EOF`. So basically, my solution works for Windows users if they press Ctrl-Z twice, and for Linux users if they press Ctrl-Z once. Right? – Kagiso Marvin Molekwa Nov 19 '18 at 11:58
  • @marvinIsSacul, just tried two consecutive Ctrl-Z's - nope. No matter how many consecutve Ctrl-Z's I put, there still needs to be an 'Enter' before a Ctrl-Z for the program to recognize an EOF. – John Allison Nov 19 '18 at 12:13
  • Mhh I see. Note I gave your question a vote up because it really opened up my eyes. I see now – Kagiso Marvin Molekwa Nov 19 '18 at 12:17