C++ Primer学习笔记 第一章 开始
自从开始学习C++已经五年时间了,之后又学习了Java、Python等语言,C++又放在一边了,硕士期间要用到,得捡起来重新温习一下,也准备写成笔记形式。
熟悉编译器
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 $?
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):使用作用域运算符::
调用。
注释
- 单行注释:
//
- 多行注释:
/**/
。编译器将/*
和*/
之间的内容都作为注释内容忽略。注意不能嵌套。
while语句
循环执行,(直到条件(condition)为假。
for语句
循环头由三部分组成:
- 一个初始化语句(init-statement)
- 一个循环条件(condition)
- 一个表达式(expression)
使用文件重定向
./main <infile >outfile
练习1.1
查阅你使用的编译器的文档,确定它所使用的文件名约定。编译并运行第2页的main程序。
解:
g++ --std=c++11 ch1.cpp -o main
./main
练习1.2
改写程序,让它返回-1。返回值-1通常被当做程序错误的标识。重新编译并运行你的程序,观察你的系统如何处理main返回的错误标识。
解:
- 在ubuntu下,使用g++,返回-1,
./main
没有发现任何异常。 -
echo $?
,返回255。
练习1.3
编写程序,在标准输出上打印Hello, World。
解:
#include <iostream>
int main()
{
std::cout << "Hello, World" << std::endl;
return 0;
}
练习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;
}
练习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;
}
练习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;
练习1.7
编译一个包含不正确的嵌套注释的程序,观察编译器返回的错误信息。
解:
/* 正常注释 /* 嵌套注释 */ 正常注释*/
错误信息:
/* 正常注释 /* 嵌套注释 */ 正常注释*/
^
ch1.cpp:97:37: error: stray ‘\255’ in program
ch1.cpp:97:37: error: stray ‘\243’ in program
ch1.cpp:97:37: error: stray ‘\345’ in program
ch1.cpp:97:37: error: stray ‘\270’ in program
ch1.cpp:97:37: error: stray ‘\270’ in program
ch1.cpp:97:37: error: stray ‘\346’ in program
ch1.cpp:97:37: error: stray ‘\263’ in program
ch1.cpp:97:37: error: stray ‘\250’ in program
ch1.cpp:97:37: error: stray ‘\351’ in program
ch1.cpp:97:37: error: stray ‘\207’ in program
ch1.cpp:97:37: error: stray ‘\212’ in 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;
^
练习1.8
指出下列哪些输出语句是合法的(如果有的话):
std::cout << "/*";
std::cout << "*/";
std::cout << /* "*/" */;
std::cout << /* "*/" /* "/*" */;
预测编译这些语句会产生什么样的结果,实际编译这些语句来验证你的答案(编写一个小程序,每次将上述一条语句作为其主体),改正每个编译错误。
解:
只有第三句编译出错,改成如下即可:
std::cout << /* "*/" */";
第四句等价于输出 " /* "
。
练习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;
}
练习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;
}
练习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 !!!";
}
}
练习1.12
下面的for循环完成了什么功能?sum的终值是多少?
int sum = 0;
for (int i = -100; i <= 100; ++i)
sum += i;
解:
从-100加到100,sum的终值是0。
练习1.13
使用for循环重做1.4.1节中的所有练习(练习1.9到1.11)。
解:
练习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;
}
练习1.10
#include <iostream>
int main()
{
for (int val = 10; val >=0; --val){
std::cout << val << " ";
}
std::cout << std::endl;
}
练习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 !!!";
}
}
练习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`.
练习1.15
编写程序,包含第14页“再探编译”中讨论的常见错误。熟悉编译器生成的错误信息。
解:
编译器可以检查出的错误有:
- 语法错误
- 类型错误
- 声明错误
练习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;
}
练习1.17
如果输入的所有值都是相等的,本节的程序会输出什么?如果没有重复值,输出又会是怎样的?
练习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
练习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 !!!";
}
}
练习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
练习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
练习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
练习1.23
编写程序,读取多条销售记录,并统计每个 ISBN(每本书)有几条销售记录。
练习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
练习1.25
借助网站上的Sales_item.h
头文件,编译并运行本节给出的书店程序。