# Chapter 1 Getting Started

# Writing a Simple C++ Program Section

Having written the program, we need to compile it. How you compile a program depends on your operating system and compiler. For details on how your particular compiler works, check the reference manual or ask a knowledgeable colleague.

Many PC-based compilers are run from an integrated development environment (IDE) that bundles the compiler with build and analysis tools. These environments can be a great asset in developing large programs but require a fair bit of time to learn how to use effectively. Learning how to use such environments is well beyond the scope of this book.

Most compilers, including those that come with an IDE, provide a command-line interface. Unless you already know the IDE, you may find it easier to start with the command-line interface. Doing so will let you concentrate on learning C++ first. Moreover, once you understand the language, the IDE is likely to be easier to learn.

Program Source File Naming Convention

Whether you use a command-line interface or an IDE, most compilers expect program source code to be stored in one or more files. Program files are normally referred to as a source files. On most systems, the name of a source file ends with a suffix, which is a period followed by one or more characters. The suffix tells the system that the file is a C++ program. Different compilers use different suffix conventions; the most common include .cc, .cxx, .cpp, .cp, and .C.

Running the Compiler from the Command Line

If we are using a command-line interface, we will typically compile a program in a console window (such as a shell window on a UNIX system or a Command Prompt window on Windows). Assuming that our main program is in a file named prog1.cc, we might compile it by using a command such as

$ CC prog1.cc

where CC names the compiler and $ is the system prompt. The compiler generates an executable file. On a Windows system, that executable file is named prog1.exe. UNIX compilers tend to put their executables in files named a.out.

To run an executable on Windows, we supply the executable file name and can omit the .exe file extension:

$ prog1

On some systems you must specify the file’s location explicitly, even if the file is in the current directory or folder. In such cases, we would write

$ .\prog1

The “.” followed by a backslash indicates that the file is in the current directory. To run an executable on UNIX, we use the full file name, including the file extension:

$ a.out

If we need to specify the file’s location, we’d use a “.” followed by a forward slash to indicate that our executable is in the current directory:

$ ./a.out

The value returned from main is accessed in a system-dependent manner. On both UNIX and Windows systems, after executing the program, you must issue an appropriate echo command.

On UNIX systems, we obtain the status by writing

$ echo $?

To see the status on a Windows system, we write

$ echo %ERRORLEVEL%

# 熟悉编译器

g++

  • 编译: g++ --std=c++11 ch01.cpp -o main
  • 运行: ./prog1
  • 查看运行状态: echo $?
  • 编译多个文件: g++ ch2.cpp Sales_item.cc -o main

输入 g++ --help ,查看编译器选项:

Usage: g++ [options] file...
Options:
  -pass-exit-codes         Exit with highest error code from a phase
  --help                   Display this information
  --target-help            Display target specific command line options
  --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
                           Display specific types of command line options
  (Use '-v --help' to display command line options of sub-processes)
  --version                Display compiler version information
  -dumpspecs               Display all of the built in spec strings
  -dumpversion             Display the version of the compiler
  -dumpmachine             Display the compiler's target processor
  -print-search-dirs       Display the directories in the compiler's search path
  -print-libgcc-file-name  Display the name of the compiler's companion library
  -print-file-name=<lib>   Display the full path to library <lib>
  -print-prog-name=<prog>  Display the full path to compiler component <prog>
  -print-multiarch         Display the target's normalized GNU triplet, used as
                           a component in the library path
  -print-multi-directory   Display the root directory for versions of libgcc
  -print-multi-lib         Display the mapping between command line options and
                           multiple library search directories
  -print-multi-os-directory Display the relative path to OS libraries
  -print-sysroot           Display the target libraries directory
  -print-sysroot-headers-suffix Display the sysroot suffix used to find headers
  -Wa,<options>            Pass comma-separated <options> on to the assembler
  -Wp,<options>            Pass comma-separated <options> on to the preprocessor
  -Wl,<options>            Pass comma-separated <options> on to the linker
  -Xassembler <arg>        Pass <arg> on to the assembler
  -Xpreprocessor <arg>     Pass <arg> on to the preprocessor
  -Xlinker <arg>           Pass <arg> on to the linker
  -save-temps              Do not delete intermediate files
  -save-temps=<arg>        Do not delete intermediate files
  -no-canonical-prefixes   Do not canonicalize paths when building relative
                           prefixes to other gcc components
  -pipe                    Use pipes rather than intermediate files
  -time                    Time the execution of each subprocess
  -specs=<file>            Override built-in specs with the contents of <file>
  -std=<standard>          Assume that the input sources are for <standard>
  --sysroot=<directory>    Use <directory> as the root directory for headers
                           and libraries
  -B <directory>           Add <directory> to the compiler's search paths
  -v                       Display the programs invoked by the compiler
  -###                     Like -v but options quoted and commands not executed
  -E                       Preprocess only; do not compile, assemble or link
  -S                       Compile only; do not assemble or link
  -c                       Compile and assemble, but do not link
  -o <file>                Place the output into <file>
  -pie                     Create a position independent executable
  -shared                  Create a shared library
  -x <language>            Specify the language of the following input files
                           Permissible languages include: c c++ assembler none
                           'none' means revert to the default behavior of
                           guessing the language based on the file's extension

输入 g++ -v --help 可以看到更完整的指令。
例如还有些常用的:

-h FILENAME, -soname FILENAME: Set internal name of shared library
-I PROGRAM, --dynamic-linker PROGRAM: Set PROGRAM as the dynamic linker to use
-l LIBNAME, --library LIBNAME: Search for library LIBNAME
-L DIRECTORY, --library-path DIRECTORY: Add DIRECTORY to library search path

获得程序状态:

  • windows: echo %ERRORLEVEL%
  • UNIX: echo $?

# Exercise 1.1

查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第 2 页的 main 程序。

解:

  • g++ --std=c++11 ch1.cpp -o main
  • ./main

# Exercise 1.2

改写程序,让它返回 - 1。返回值 - 1 通常被当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理 main 返回的错误标识。

解:

  • 在 ubuntu 下,使用 g++,返回 - 1, ./main 没有发现任何异常。
  • echo $? ,返回 255。

# A First Look at Input/Output Section

The C++ language does not define any statements to do input or output (IO). Instead, C++ includes an extensive standard library that provides IO (and many other facilities). For many purposes, including the examples in this book, one needs to know only a few basic concepts and operations from the IO library.

Most of the examples in this book use the iostream library. Fundamental to the iostream library are two types named istream and ostream, which represent input and output streams, respectively. A stream is a sequence of characters read from or written to an IO device. The term stream is intended to suggest that the characters are generated, or consumed, sequentially over time.

Standard Input and Output Objects

The library defines four IO objects. To handle input, we use an object of type istream named cin (pronounced see-in). This object is also referred to as the standard input. For output, we use an ostream object named cout (pronounced see-out). This object is also known as the standard output. The library also defines two other ostream objects, named cerr and clog (pronounced see-err and see-log, respectively). We typically use cerr, referred to as the standard error, for warning and error messages and clog for general information about the execution of the program.

Ordinarily, the system associates each of these objects with the window in which the program is executed. So, when we read from cin, data are read from the window in which the program is executing, and when we write to cout, cerr, or clog, the output is written to the same window.


# IO

  • #include <iostream>
  • std::cout << "hello"
  • std::cin >> v1

记住 >><< 返回的结果都是左操作数,也就是输入流和输出流本身。

endl:这是一个被称为操纵符(manipulator)的特殊值,效果是结束当前行,并将设备关联的缓冲区(buffer)中的内容刷到设备中。

UNIX 和 Mac 下键盘输入文件结束符: ctrl+d ,Windows 下: ctrl+z

头文件:类的类型一般存储在头文件中,标准库的头文件使用 <> ,非标准库的头文件使用 "" 。申明写在 .h 文件,定义实现写在 .cpp 文件。

避免多次包含同一头文件

#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif

成员函数(类方法):使用 . 调用。

命名空间(namespace):使用作用域运算符 :: 调用。

从键盘输入文件结束符:当从键盘向程序输入数据时,对于如何指出文件结束,不同操作系统有不同的约定。在 Windows 系统中,输入文件结束符的方法是敲 Ctrl+Z(按住 Ctrl 键的同时按 Z),然后按 Enter 或 Return 键。在 UNIX 系统中,包括 Mac OS X 系统中,文件结束符输入是用 Ctrl+D。


# Exercise 1.3

编写程序,在标准输出上打印 Hello, World。

解:

#include <iostream>
int main()
{
	std::cout << "Hello, World" << std::endl;
	return 0;
}

# Exercise 1.4

我们的程序使用加法运算符 + 来将两个数相加。编写程序使用乘法运算符 * ,来打印两个数的积。

解:

#include <iostream>
int main()
{
    std::cout << "Enter two numbers:" << std::endl;
    int v1 = 0, v2 = 0;
    std::cin >> v1 >> v2;
    std::cout << "The product of " << v1 << " and " << v2
              << " is " << v1 * v2 << std::endl;
}

# Exercise 1.5

我们将所有的输出操作放在一条很长的语句中,重写程序,将每个运算对象的打印操作放在一条独立的语句中。

解:

#include <iostream>
int main()
{
    std::cout << "Enter two numbers:" << std::endl;
        int v1 = 0, v2 = 0;
        std::cin >> v1 >> v2;
        std::cout << "The product of ";
        std::cout << v1;
        std::cout << " and ";
        std::cout << v2;
        std::cout << " is ";
        std::cout << v1 * v2;
        std::cout << std::endl;
}

# Exercise 1.6

解释下面程序片段是否合法。

std::cout << "The sum of " << v1;
          << " and " << v2;
          << " is " << v1 + v2 << std::endl;

如果程序是合法的,它的输出是什么?如果程序不合法,原因何在?应该如何修正?

解:

程序不合法,有多余的分号,修改如下:

std::cout << "The sum of " << v1
          << " and " << v2
          << " is " << v1 + v2 << std::endl;

# A Word about Comments Section

Kinds of Comments in C++

There are two kinds of comments in C++: single-line and paired. A single-line comment starts with a double slash (//) and ends with a newline. Everything to the right of the slashes on the current line is ignored by the compiler. A comment of this kind can contain any text, including additional double slashes.

The other kind of comment uses two delimiters (/* and /) that are inherited from C. Such comments begin with a / and end with the next */. These comments can include anything that is not a /, including newlines. The compiler treats everything that falls between the / and */ as part of the comment.

A comment pair can be placed anywhere a tab, space, or newline is permitted. Comment pairs can span multiple lines of a program but are not required to do so. When a comment pair does span multiple lines, it is often a good idea to indicate visually that the inner lines are part of a multiline comment. Our style is to begin each line in the comment with an asterisk, thus indicating that the entire range is part of a multiline comment.


# 注释

  • 单行注释: //
  • 多行注释: /**/ 。编译器将 /**/ 之间的内容都作为注释内容忽略。注意不能嵌套。
#define SALESITEM_H
/*
 * 多行注释格式
 * 每一行加一个 *
 */

# Exercise 1.7

编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。

解:

/* 正常注释 /* 嵌套注释 */ 正常注释*/

错误信息:

/* 正常注释 /* 嵌套注释 */ 正常注释*/
                                     ^
ch1.cpp:97:37: error: stray ‘\255in program
ch1.cpp:97:37: error: stray ‘\243in program
ch1.cpp:97:37: error: stray ‘\345in program
ch1.cpp:97:37: error: stray ‘\270in program
ch1.cpp:97:37: error: stray ‘\270in program
ch1.cpp:97:37: error: stray ‘\346in program
ch1.cpp:97:37: error: stray ‘\263in program
ch1.cpp:97:37: error: stray ‘\250in program
ch1.cpp:97:37: error: stray ‘\351in program
ch1.cpp:97:37: error: stray ‘\207in program
ch1.cpp:97:37: error: stray ‘\212in program
ch1.cpp: In function ‘int main()’:
ch1.cpp:97:50: error: expected primary-expression before ‘/’ token
  /* 正常注释 /* 嵌套注释 */ 正常注释*/
                                                  ^
ch1.cpp:98:5: error: expected primary-expression before ‘return’
     return 0;
     ^

# Exercise 1.8

指出下列哪些输出语句是合法的(如果有的话):

std::cout << "/*";
std::cout << "*/";
std::cout << /* "*/" */;
std::cout << /* "*/" /* "/*" */;

预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案 (编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。

解:

只有第三句编译出错,改成如下即可:

std::cout << /* "*/" */";

第四句等价于输出 " /* "

# Flow of Control Section


# while 语句

循环执行,(直到条件(condition)为假。

# for 语句

循环头由三部分组成:

  • 一个初始化语句(init-statement)
  • 一个循环条件(condition)
  • 一个表达式(expression)

# Exercise 1.9

编写程序,使用 while 循环将 50 到 100 整数相加。

解:

#include <iostream>
int main()
{
    int sum = 0, val = 50;
    while (val <= 100){
        sum += val;
        val += 1;
    }
    std::cout << "Sum of 50 to 100 inclusive is "
              << sum << std::endl;
}

# Exercise 1.10

除了 ++ 运算符将运算对象的值增加 1 之外,还有一个递减运算符 -- 实现将值减少 1. 编写程序与,使用递减运算符在循环中按递减顺序打印出 10 到 0 之间的整数。

解:

#include <iostream>
int main()
{
    int val = 10;
    while (val >= 0){
        std::cout << val << " ";
        val -= 1;
    }
    std::cout << std::endl;
}

# Exercise 1.11

编写程序,提示用户输入两个整数,打印出这两个整数所指定的范围内的所有整数。

解:

#include <iostream>
int main()
{
    int start = 0, end = 0;
    std::cout << "Please input two num: ";
    std::cin >> start >> end;
    if (start <= end) {
        while (start <= end){
            std::cout << start << " ";
            ++start;
        }
        std::cout << std::endl;
    }
    else{
        std::cout << "start should be smaller than end !!!";
    }
}

# Exercise 1.12

下面的 for 循环完成了什么功能?sum 的终值是多少?

int sum = 0;
for (int i = -100; i <= 100; ++i)
	sum += i;

解:

从 - 100 加到 100,sum 的终值是 0。

# Exercise 1.13

使用 for 循环重做 1.4.1 节中的所有练习(练习 1.9 到 1.11)。

解:

# Exercise 1.9

#include <iostream>
int main()
{
    int sum = 0;
    for (int val = 50; val <= 100; ++val){
        sum += val;
    }
    std::cout << "Sum of 50 to 100 inclusive is "
              << sum << std::endl;
}

# Exercise 1.10

#include <iostream>
int main()
{
    for (int val = 10; val >=0; --val){
        std::cout << val << " ";
    }
    std::cout << std::endl;
}

# Exercise 1.11

#include <iostream>
int main()
{
    int start = 0, end = 0;
    std::cout << "Please input two num: ";
    std::cin >> start >> end;
    if (start <= end) {
        for (; start <= end; ++start){
            std::cout << start << " ";
        }
        std::cout << std::endl;
    }
    else{
        std::cout << "start should be smaller than end !!!";
    }
}

# Exercise 1.14

对比 for 循环和 while 循环,两种形式的优缺点各是什么?

解:

The main difference between the `for`'s and the `while`'s is a matter of pragmatics: 
we usually use `for` when there is a known number of iterations, 
and use `while` constructs when the number of iterations in not known in advance. 
The `while` vs `do ... while` issue is also of pragmatics, 
the second executes the instructions once at start, 
and afterwards it behaves just like the simple `while`.

# Exercise 1.15

编写程序,包含第 14 页 “再探编译” 中讨论的常见错误。熟悉编译器生成的错误信息。

解:

编译器可以检查出的错误有:

  • 语法错误
  • 类型错误
  • 声明错误

# Exercise 1.16

编写程序,从 cin 读取一组数,输出其和。

解:

#include <iostream>
int main()
{
    int sum = 0;
    for (int value = 0; std::cin >> value; )
        sum += value;
    std::cout << sum << std::endl;
    return 0;
}

# Exercise 1.17

如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?

# Exercise 1.18

编译并运行本节的程序,给它输入全都相等的值。再次运行程序,输入没有重复的值。

解:

全部重复:

1 1 1 1 1 
1 occurs 5 times

没有重复:

1 2 3 4 5
1 occurs 1 times 
2 occurs 1 times 
3 occurs 1 times 
4 occurs 1 times 
5 occurs 1 times

# Exercise 1.19

修改你为 1.4.1 节练习 1.11(第 11 页)所编写的程序(打印一个范围内的数),使其能处理用户输入的第一个数比第二个数小的情况。

解:

#include <iostream>
int main()
{
    int start = 0, end = 0;
    std::cout << "Please input two num: ";
    std::cin >> start >> end;
    if (start <= end) {
        while (start <= end){
            std::cout << start << " ";
            ++start;
        }
        std::cout << std::endl;
    }
    else{
        std::cout << "start should be smaller than end !!!";
    }
}

# Introducing Classes Section


# 使用文件重定向

当你测试程序时,反复从键盘敲入记录作为程序的输入,是非常乏味的。大多数操作系统支持文件重定向,这种机制允许我们将标准输入和标准输出与命名文件关联起来:

$ ./main <infile >outfile

假定 $ 是操作系统提示符,我们的加法程序已经编译为名


# Exercise 1.20

在网站 http://www.informit.com/title/032174113 上,第 1 章的代码目录包含了头文件 Sales_item.h。将它拷贝到你自己的工作目录中。用它编写一个程序,读取一组书籍销售记录,将每条记录打印到标准输出上。

解:

#include <iostream>
#include "Sales_item.h"
int main()
{
	for (Sales_item item; std::cin >> item; std::cout << item << std::endl);
	return 0;
}

命令:

./main < data/add_item

输出:

0-201-78345-X 3 60 20
0-201-78345-X 2 50 25

# Exercise 1.21

编写程序,读取两个 ISBN 相同的 Sales_item 对象,输出他们的和。

解:

#include <iostream>
#include "Sales_item.h"
int main()
{
    Sales_item item_1;
    Sales_item item_2;
    std::cin >> item_1;
    std::cout << item_1 << std::endl;
    std::cin >> item_2;
    std::cout << item_2 << std::endl;
    std::cout << "sum of sale items: " << item_1 + item_2 << std::endl;
	return 0;
}

命令:

./main < data/add_item

输出:

0-201-78345-X 3 60 20
0-201-78345-X 2 50 25
sum of sale items: 0-201-78345-X 5 110 22

# Exercise 1.22

编写程序,读取多个具有相同 ISBN 的销售记录,输出所有记录的和。

解:

#include <iostream>
#include "Sales_item.h"
int main()
{
    Sales_item sum_item;
    std::cin >> sum_item;
    std::cout << sum_item << std::endl;
    for (Sales_item item; std::cin >> item; std::cout << item << std::endl){
        sum_item += item;
    }
    std::cout << "sum of sale items: " << sum_item << std::endl;
	return 0;
}

命令:

./main < data/add_item

输出:

0-201-78345-X 3 60 20
0-201-78345-X 2 50 25
sum of sale items: 0-201-78345-X 5 110 22

# Exercise 1.23

编写程序,读取多条销售记录,并统计每个 ISBN(每本书)有几条销售记录。

# Exercise 1.24

输入表示多个 ISBN 的多条销售记录来测试上一个程序,每个 ISBN 的记录应该聚在一起。

解:

#include <iostream>
#include "Sales_item.h"
int main()
{
    Sales_item total;
    if (std::cin >> total){
        Sales_item trans;
        while (std::cin >> trans){
            if (total.isbn() == trans.isbn()) {
                total += trans;
            }
            else {
                std::cout << total << std::endl;
                total = trans;
            }
        }
        std::cout << total << std::endl;
    }
    else {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
    return 0;
}

命令:

./main < data/book_sales

输出:

0-201-70353-X 4 99.96 24.99
0-201-82470-1 4 181.56 45.39
0-201-88954-4 16 198 12.375
0-399-82477-1 5 226.95 45.39
0-201-78345-X 5 110 22

# The Bookstore Program


# Exercise 1.25

借助网站上的 Sales_item.h 头文件,编译并运行本节给出的书店程序。

# Chapter Summary

🍓:)本章介绍了如何编译、运行简单的 C++ 程序。我们看到了如何定义一个 main 函数,它是操作系统执行程序的调用入口。我们还看到了如何定义变量,如何进行输入输出,以及如何编写 if、for 和 while 语句。以及对于其他人定义的一个类,我们应该如何创建、使用其对象。