Exercises and Solutions of Chapter 1

  • Exercise 1-0——Test chapter programs
  • Exercise 1-1——Understanding string concatenation
  • Exercise 1-2——Understanding string concatenation
  • Exercise 1-3——Understanding scope
  • Exercise 1-4——Understanding scope
  • Exercise 1-5——Understanding scope
  • Exercise 1-6——Explore the “hidden” std::cin buffer operations

1-0 Compile, execute, and test the programs in this chapter.

Solution:

Program 1: say hello to a specific person. It will ask for a person’s name and generate a specific greeting as its output.

// ask for a person's name, and greet the person
#include <iostream>
#include <string>
int main()
{
    // ask for the person's name
    std::cout << "Please enter your first name: ";
    // read the name
    std::string name; // define name
    std::cin >> name; // read into
    // write a greeting
    std::cout << "Hello, " << name << "!" << std::endl;
    return 0;
}

7314beb7efbe4573999deeb703e514b5.png

Program 2: a more elaborate greeting. It will produce five lines of output. The first line begins the frame. It is a sequence of * characters as long as the greeting, plus a space and an * at each end. The second line will be an appropriate number of spaces with an * at each end. The third line is an *, a space, the greeting, a space, and an *. The last two lines will be the same as the second and first lines, respectively.

// ask for a person's name, and generate a framed greeting
#include <iostream>
#include <string>
int main()
{
    std::cout << "Please enter your first name: ";
    std::string name;
    std::cin >> name;

    // build the message that we intend to write
    const std::string greeting = "Hello, " + name + "!";
 
    // build the second and fourth lines of the output
    const std::string spaces(greeting.size(), ' ');
    const std::string second = "* " + spaces + " *";

    // build the first and fifth lines of the output
    const std::string first(second.size(), '*');

    // write it all
    std::cout << std::endl;
    std::cout << first << std::endl;
    std::cout << second << std::endl;
    std::cout << "* " << greeting << " *" << std::endl;
    std::cout << second << std::endl;
    std::cout << first << std::endl;
 
    return 0;
}

b4b61ef95f4e403f85984f46ebde7d29.png

1-1 Are the following definitions valid? Why or why not?

const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";

Solution:

Yes, these definitions are valid. Line 1 defines a string variable hello with length of 5 characters (which is “Hello”). Line 2 defines a string variable message with the concatenation operator. The logic looks like this:

message = ( ( hello + ", world" ) + "!")
        = ( ( a string + a string literal ) + a string literal )
        = ( ( a string ) + a string literal )
        = ( a string + a string literal )
        = ( a string )

The key in answering this question is to acknowledge the use of the string concatenation operator +.

  1. It is left associative.
  2. We can use + to concatenate a string and a string literal (and vice versa), or a string and a string, but not a string with a string literal (nor vice versa).

We could verify our conclusion by running this program:

#include <iostream>
#include <string>
int main()
{
    const std::string hello = "Hello";
    const std::string message = hello + ", world" + "!";
    std::cout << message << std::endl;
    return 0;
}

1-2 Are the following definitions valid? Why or why not?

const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;

Solution:

No, the use of the concatenation operator is not valid. i.e. we bump into a “taboo” scenario: string literal + string literal, which is not valid.

  • Line 1 defines a string variable exclam, which is valid.
  • Line 2 defines a string variable message with the concatenation operator. The logic looks like this:
message = ( ( "Hello " + ", world" ) + exclam)
        = ( ( a string literal + a string literal ) + a string )
        = ( ( compilation error! ) + a string)

We could verify our conclusion by running this program:

#include <iostream>
#include <string>
int main()
{
    const std::string exclam = "!";
    const std::string message = "Hello  + ", world" + exclam;
    std::cout << message << std::endl;
    return 0;
}

The compilation error may look like this:

error: invalid operands of types 'const char [6]' and 'const char [8]' to binary 'operator+'

1-3 Is the following program valid?

#include <iostream>
#include <string>
int main()
{
 { const std::string s = "a string";
 std::cout << s << std::endl; }
 
 { const std::string s = "another string";
 std::cout << s << std::endl; }

 return 0;
}

Solution:

Yes, the program is valid. The key to this question is to understanding the term scope. Each pair of curly braces form a scope. Within the main function (scope), we have two sub-scopes (block), as defined by the two pairs of curly braces. Each block constitute its own scope——all local variables and statements within each scope are independent to each other. Because of this, even though there is a const std::string variable eq?s in block 1, it is okay to define another const std::string variable eq?s in block 2——due to the fact the two variables are in different scopes.

 

1-4 Is the following program valid? What if we add a semi-colon (;) between the second-last and third-last right curly braces?

#include <iostream>

#include <string>

int main()
{
    {
        const std::string s = "a string";
        std::cout << s << std::endl;
        { 
            const std::string s = "another string";
            std::cout << s << std::endl;
        }
    }
}

Solution:

Part one:

Yes, the program is valid. Each pair of curly braces {} form a scope. It is okay to have scopes nested within a scope. For clarity let me add some comments to the code to visualise these scopes:

#include <iostream>
#include <string>
int main()
{//scope main starts
    {   //scope main-1 start
        const std::string s = "a string";
        std::cout << s << std::endl;
        {   //scope main-1-1 starts
            const std::string s = "another string";
            std::cout << s << std::endl;
        }  //scope main-1-1 ends
    }  //scope main-1 ends
} //scope main ends

The const std::string variable eq?s in scope main-1 is not the same const std::string variable eq?s in scope main-1-1 (which is nested inside scope main-1). Even though scope main-1-1 is nested inside scope main-1, all local variables inside scope main-1-1 is hidden from view of main-1.

Notably, string eq?s is not overwritten in the inner scope——it is still available from the outer scope.

 

Part two:

Adding a semi-colon (;) between the second last and third last right curly braces will still constitute a valid program. For clarity, this is what the program would look like with that semi-colon:

#include <iostream>
#include <string>
int main()
{//scope main starts
    {   //scope main-1 start
        const std::string s = "a string";
        std::cout << s << std::endl;
        {   //scope main-1-1 starts
            const std::string s = "another string";
            std::cout << s << std::endl;
        }  //scope main-1-1 ends
        ;  // the additional semi-colon
    }  //scope main-1 ends
} //scope main ends

The additional semi-colon essentially creates a null-statement within the main-1 scope. i.e. it has no effect to the code. I think the reason the authors ask this question is to solidify our understanding on scope. i.e. which scope does that semi-colon belong to? Writing a C++ code in the above manner helps us visualise this easier.

 

1-5 Is this program valid?

#include <iostream>
#include <string>
int main()
{
    {
        std::string s = "a string";
        {
            std::string x = s + ", really";
            std::cout << s << std::endl;
        }
        std::cout << x << std::endl;
    }
    return 0;
}

Solution:

No. The program is not valid and requires correction. Notably, one scope may not “see” what’s inside the other scope(s). For clarity let me add some comments to the code to visualise these scopes:

// original program with comments added to visualise scope
#include <iostream>
#include <string>
int main()
{ // scope main starts
    { // scope main-1 starts
        std::string s = "a string";
        { // scope main-1-1 starts
            std::string x = s + ", really";
            std::cout << s << std::endl;
        } //scope main-1-1 ends
        std::cout << x << std::endl;
    } // scope main-1 ends
    return 0;
} // scope main ends

All local variables defined at the outer scope level may be seen/used by the inner scopes (at all levels); However, the reverse is not possible. i.e. All local variables defined at the inner scope level may NOT be seen/used by the outer scopes (at all levels), nor the scopes adjacent to it. (i.e. same level scopes). The permeation of variables go from outer scope, to inner scope. It does not permeate to other scopes at the same level, and/or the inner scopes (at all levels).

scope main-1-1 can see std::string variable s (which is defined in the outer scope main-1), and its own defined std::string variable x.

scope main-1 can see only its own defined variable std::string s. It cannot see the std::string variable x that lives at the inner scope main-1-1 level. It fails to perform that “std::cout << x << std::endl” step because it doesn’t know what x is. From scope main-1’s perspective, the variable x is not declared.

scope main has no idea of any variables defined in the inner scopes main-1 nor main-1-1. All it knows is that whenever the implementation hits the return statement, it is done.

The compilation error may look like this:

line(12): error: ‘x’ was not declared in this scope

To make the std::string x visible to the scope main-1, we can simply move the scope main-1-1 up one level by removing the curly braces. Like this:

// corrected
#include <iostream>
#include <string>
int main()
{ // scope main starts
    { // scope main-1 starts
        std::string s = "a string";
        std::string x = s + ", really";
        std::cout << s << std::endl;
        std::cout << x << std::endl;
    } // scope main-1 ends
    return 0;
} // scope main ends

1-6 What does the following program do if, when it asks you for input, you type two names at a time(for example, Samuel Beckett)? Predict the behavior before running the program, then try it.

#include <iostream>
#include <string>
int main()
{
    std::cout << "What is your name? ";
    std::string name;
    std::cin >> name; // first std::cin step
    std::cout << " Hello, " << name
              << std::endl  << "And what is yours?";
    std::cin >> name;  // second std::cin step
    std::cout << "Hello, " << name
              << "; nice to meet you too!" << std::endl;
    
    return 0;
}

Solution:

At the first std::cin step we type in two words, “Samuel Beckett”, followed by hitting the enter key. The following happens:

(1) The two words initially get stored in the buffer (corresponding to std::string variable name)

Samuel Beckett

Note that the buffer automatically discard the leading and trailing blanks. Each word is separated by a space character.

(2) The first std::cin step causes the buffer to flush out the first word “Samuel” to the std::string variable assignment. The std::string name has now a value of “Samuel”. Because the first word has now been flushed out from the buffer, the buffer for name now looks like this:

Beckett

Note that the first word Samuel is now gone. It has been flushed away from the buffer.

(3) At the second std::cin step, becuase there is already a word in the buffer, it just simply asks the buffer to flush out that value and assign that to the std::string variable name. The std::string name has now a value of “Beckett” (and replace the old value “Samuel”). Now, the buffer for name looks like this:

 

The buffer is now empty!

If there turn out to be a third std::cin step, the user will be asked to supply more values to the buffer, so that the std::cin facility may read from it. (But in this case, the program only has two std::cin steps).

In summary, the std::cin checks the buffer corresponding to the std::string variable:

  1. If there is at least one word stored inside the buffer, it flushes the first word from the buffer, read and assign that word to the std::string variable. The program does not need to pause and ask the user to supply values.
  2. If the buffer is empty, the program would pause and ask the user to supply values (words) to the buffer. Only when there are words inside the buffer, the std::cin would then read from it and assign the value to the std::string variable (and flush that word away from the buffer as a consequence).

For completeness, let us run the program and confirm our understanding:

9f1d0b14dd124f68bdfdb8cad7f120f2.png

Notably, if, we provide no word at the first std::cin step, we will find that hitting the enter button does not get us anywhere——the program insists us to provide at least one word to the buffer.

猜你喜欢

转载自blog.csdn.net/ycy1300585044/article/details/132745870