0
#include <iostream>

using namespace std;

int output_1(string s, char c);
int output_2(string s, char c);

int main() {

    string str;
    char c;

    cout << "Enter a string: ";
    getline(cin, str);

    cout << "Enter a character: ";
    cin >> c;

    cout << "[1] the number of times the character appears: " << output_1(str, c) << endl;
    
    cout << "[2] That character is found at Index/Indices: " << output_2(str, c) << endl;

    return 0;
}

int output_1(string s, char c) {

    int count = 0;

    for (int x = 0; x < s.length(); x++)

        if (s[x] == c)
            count++;

    return count;
}

int output_2(string s, char c) {
    
    for (int x = 0; x < s.length(); x++) {
        if (c == s[x]){
            cout << x << " ";
        }
    }
}

Why is the second output not in order and have an extra 0 at the end?

Enter a string: test
Enter a character: t
[1] the number of times the character appears: 2
0 3 [2] That character is found at Index/Indices: 0

  • Turn on your compiler warnings. It would have saved you this entire ordeal. – chris Feb 05 '21 at 18:24
  • A question like this would be much clearer if the code simply initialized `str` and `c` rather than prompting for input. That way readers can see exactly what’s going on from the code itself without having to go back and forth between the code and the text. And it makes testing the code much simpler: just cut and paste, compile it, and run it. – Pete Becker Feb 05 '21 at 18:53

5 Answers5

1

The problem is this line:

cout << "[2] That character is found at Index/Indices: " << output_2(str, c) << endl;

The first thing to note is that << is just syntactic sugar for a function call. So we can re-write it to look like this:

cout.op<<("[2] That character is found at Index/Indices: ")
    .op<<(output_2(str, c))
    .op<<(endl);

Because they are chained statements you can think of this as:

std::ostream& t1 = cout.op<<("[2] That character is found at Index/Indices: ");
std::ostream& t2 = t1.op<<(output_2(str, c));
std::ostream& t3 = t2.op<<(endl);

So what we have are three method calls to op<<() each passing one parameter. Before calling these methods that the compiler must get the operands to the calls. The first one is easy as it is a literal string, the second one requires a function call. So the compiler must call the function output_2() before it can call op<<() and the third one is an function (easy to pass the address).

But the compiler can sequence that call to output_2() at any point (as long as it happens before the call to op<<(). So it looks the compiler is doing this:

<Seq Start>    
void&         x  = output_2(str, c); // yes I know thats not legal C++ just for explaining.
std::ostream& t1 = cout.op<<("[2] That character is found at Index/Indices: ");
std::ostream& t2 = t1.op<<(x);
std::ostream& t3 = t2.op<<(endl);
<Seq End>

Note: The compiler could place the call anywhere between <Seq Start> and <Seq End> (before it is needed) it's just an implementation detail. Now the call to output_2() also actually outputs to the std::cout followed by a int (which is not returned (undefined behavior by itself)).

Martin York
  • 234,851
  • 74
  • 306
  • 532
  • not sure if I get the point of the last paragraph. It can be anywhere between `` and `` but `void& x = output_2(...)` must be before `std::ostream& t2 = t1.op< – 463035818_is_not_a_number Feb 05 '21 at 19:19
  • @largest_prime_is_463035818 The code above is still very granular that only shows two places to put the call. In reality we could break that down even further allowing more points to put it. But it can be sequenced anywhere before it is used. I have added some text. – Martin York Feb 05 '21 at 19:39
  • I believe the issue is much simpler: `output_2` lies and doesn't return a value. It should be removed from the `cout` statement and placed as a separate statement. It should also have a return type of `void`. A function that says it returns an `int` but doesn't, exhibits undefined behavior. – Thomas Matthews Feb 05 '21 at 19:43
0

Instead of printing value in main, Try this :

//call this from main 

    output_1(str, c);
    output_2(str, c);

Then:

void output_1(string s, char c) {

    int count = 0;

    for (int x = 0; x < s.length(); x++)

        if (s[x] == c)
            count++;

    cout<< count<<endl;
}

void output_2(string s, char c) {
    
    for (int x = 0; x < s.length(); x++) {
        if (c == s[x]){
            cout << x << " ";
        }
    }
}
Azahar Alam
  • 571
  • 2
  • 14
0

This is UB. output_2 should return an int, but doesn't return anything. Now you get a 0, but it could be anything.

As you're building op an ostream in output_2, you could use a stringstream internally and convert that to a string for the output.

#include<string>
#include<iostream>
#include<algorithm>
#include<sstream>

int output_1(std::string const& s, char c) {
    return std::count(cbegin(s), cend(s), c);
}

std::string output_2(std::string const& s, char c) {
    std::stringstream os;
    for (int x = 0; x < s.length(); ++x) {
        if (c == s[x]){
            os << x << " ";
        }
    }
    return os.str();
}

int main() {
    std::string str = "test";
    char c = 't';

    std::cout << "[1] the number of times the character appears: " << output_1(str, c) << '\n';
    
    std::cout << "[2] That character is found at Index/Indices: " << output_2(str, c) << '\n';
}
JHBonarius
  • 6,889
  • 3
  • 12
  • 28
0

You issue is that output_2 lies. The return type of int says the function returns a value. There is no return statement, so there is undefined behavior.

My suggested solution:

void output_2(string s, char c)
{
    for (int x = 0; x < s.length(); x++)
    {
        if (c == s[x])
        {
            cout << x << " ";
        }
    }
}

int main()
{
  //...
    cout << "[1] the number of times the character appears: " << output_1(str, c) << endl;
    
    cout << "[2] That character is found at Index/Indices: ";
    output_2(str, c);
    cout << endl;

    return 0;
}

In the above code fragments, the return type of output_2 is set to void since it doesn't return a value.

In the main function, the output_2() is removed from the cout statement (since output_2() doesn't return a value), and set as a separate function call.

Thomas Matthews
  • 52,985
  • 12
  • 85
  • 144
-1

Running through output_2():

int output_2(string s, char c) {

for (int x = 0; x < s.length(); x++) {
    if (c == s[x]){
        cout << x << " ";
    }
}

}

First pass (t) prints 0.

Second pass (e) prints nothing.

Third pass (s) prints nothing

Fourth pass (t) prints 3

You wrote output_2() as returning int, but actually you should be returning an array of integers and printing those (or something similar). The return value of a function that is supposed to return an int but has no return line is undefined. The behaviour of the whole program is also undefined because of this missing return statement. Anyway, in your specific case, it looks like output_2() returned 0 and so finally after control was returned to main it printed

cout << "[2] That character is found at Index/Indices: " << output_2(str, c) << endl;

where output_2(str, c) prints a zero (in an undefined manner). I think one thing that you were missing (with respect to order) is that the line

cout << "[2] That character is found at Index/Indices: " << output_2(str, c) << endl;

first allows output_2() to print 0 and 3 and then starts to print this line.

  • 2
    "The default return of a function that is supposed to return an int but has no return line is zero" No. Thats wrong. There is no such thing as a "default return". If it is declared to return an `int` but does not the code has undefined behavior – 463035818_is_not_a_number Feb 05 '21 at 18:27
  • 2
    The only non-void function that has an implicit return value if there is no `return` statement is `main()`. – Blastfurnace Feb 05 '21 at 18:29
  • It's not just that the return value is undefined, it means the whole program has [undefined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior). – Blastfurnace Feb 05 '21 at 18:37
  • Yes. In the sense that we don't know what it will do beings as there was no return line. – TrancendentalObject Feb 05 '21 at 18:41