Chapter 1. Getting Started
1.1 Writing a simple C++ Program
对于main函数而言,return 0表示一切正常,非0值表示有情况,具体含义由系统定义。
Exercise 1.1: Review the documentation for your compiler and determine what file naming convention it uses. Compile and run the main program from page 2.
.c 后缀,C语言源代码文件;
.C .cc .cxx后缀,C++源代码文件;
.o 后缀,编译后目标文件;
.a 后缀,由目标文件构成的库文件,由.o文件合并构成;
.h 后缀,头文件;
.i 后缀,经过预处理的C源代码文件;
.ii后缀,经过预处理过的C++源代码文件;
.s 后缀,是汇编语言源代码文件;
.S 后缀,是经过预编译的汇编语言源代码文件。
gcc编译程序,经历如下过程:
- 预处理 preprocess 将源码文件中的头文件、宏等信息进行替换
# gcc -E prog1.cc -o prog1.i
# ls
prog1.cc prog1.i
- 编译 compile 编译过程负责将经过预处理的文件编译为汇编文件
# gcc -S prog1.i -o prog1.s
# ls
prog1.cc prog1.h prog1.i prog1.s
# vim prog1.s
.file "prog1.cc"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $10, -16(%rbp)
movl $11, -12(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.1"
.section .note.GNU-stack,"",@progbits
~
- 汇编 assemble
将汇编文件处理为目标文件,可以看到目录中有prog1.o
# gcc -c prog1.s -o prog1.o
# ls
prog1 prog1.cc prog1.h prog1.i prog1.o prog1.s prog_o1
- 链接 link
链接负责将目标文件链接为可执行文件
# gcc prog1.o -o prog1
# ll -h
total 444K
-rwxr-xr-x 1 root root 6.4K Mar 29 22:40 prog1
-rw-r--r-- 1 root root 89 Mar 29 22:34 prog1.cc
-rw-r--r-- 1 root root 54 Mar 29 22:33 prog1.h
-rw-r--r-- 1 root root 208 Mar 29 22:34 prog1.i
-rw-r--r-- 1 root root 1.2K Mar 29 22:39 prog1.o
-rw-r--r-- 1 root root 387 Mar 29 22:35 prog1.s
-rw-r--r-- 1 root root 414K Mar 29 22:22 prog_o1
# ./prog1
Exercise 1.2: Change the program to return -1. A return value of -1 is often treated as an indicator that the program failed. Recompile and return your program to see how your system treats a failure indicator from main.
将某个示例程序返回值改为return -1,重新编译并看系统如何对待这个错误。
# ./prog1
# echo $?
255
根据截图可以看到,255对应返回值-1,意义为Exit status out of range
我们在实际开发中,还是要根据具体的错误,返回具体的错误编码。
##1.2 A First Look at Input/Output 编程小点 示例代码中采用std::cout, std::cin, std::endl进行io相关操作,目的是明确指明使用的namespace。这样的好处可以避免重名的冲突。
在代码中我们经常使用std::cout << "hello" << "world.", std::cin>>a>>b的类似操作,是因为操作符 << >> 会返回他的左边的参数,作为下一个操作符的左边参数之用。
对于/** 类型的注释,拒绝嵌套使用
1.4 控制结构
在命令式的编程语言中,少不了控制结构,c, c++, java, python, go等中都是如此。我们最常用的集中如下:
while循环 (我基本不用do while这种结构)
while(condition)
{
do while_statement_body;
}
for循环
for(初始化语句;条件判断;表达式)
{
do for_statement_body;
}
if/else结构
if else属于分支判断,用的非常多,不在细说。
switch case结构 switch case同样属于if else的一种类型。我在工作中往往用if/else用的比较多,switch case用的比较少。
1.5 类
类是面向对象语言中一个非常重要的特性,在C语言中我们定义结构体抽象出实体,通过函数的执行带动数据的流动,用数据流图描述更为合适;在面向对象中,我们赋予对象行为,让其具备特征的同时,可以通过行为去改变特征,我的理解是通过抽象进行特征提取,实现代码的共用,去除冗余。
对应习题的对Sales_item的初步实现:
#include <iostream>
#include <string>
class Sales_item
{
public:
Sales_item& operator=(Sales_item& item);
Sales_item& operator+=(Sales_item& item);
std::string get_isbn();
public:
friend std::istream& operator >>(std::istream& in, Sales_item& item);
friend std::ostream& operator<<(std::ostream& out, Sales_item& item);
private:
std::string isbn;
int32_t count;
float price;
};
std::istream& operator>>(std::istream & in, Sales_item& item)
{
std::cout << "输入订单信息" << std::endl;
in >> item.isbn >> item.count >> item.price;
return in;
}
std::ostream& operator<<(std::ostream& out, Sales_item& item)
{
out << item.isbn << " " << item.count << " " << item.price << " " << item.count * item.price << std::endl;
return out;
}
Sales_item& Sales_item::operator=(Sales_item& item)
{
this->isbn = item.isbn;
this->count = item.count;
this->price = item.price;
return *this;
}
Sales_item& Sales_item::operator+=(Sales_item& item)
{
if (this->isbn.compare(item.isbn) == 0)
{
this->count += item.count;
}
return *this;
}
std::string Sales_item::get_isbn()
{
return isbn;
}
我们可以尝试总结一般性的命令式编程语言学习方法:
- 首先了解语言提供的内置数据类型,了解不同类型数据的定义及初始化方法,所占用的数据长度,取值范围等;
- 学习编程语言提供的控制结构,其实上文中已经罗列了一般性的结构,顺序,循环,分支。
- 学习语言特性,面向对象语言?如何定义类,属性,方法,类的组织和分工,可以结合UML中的类图分析类之间的关系,可以结合设计模式去组织你的类。
- 学习该语言所提供的核心库功能,比如io,alogrithm, 容器,多线程,并发等和实际系统开发密切相关的内容;
- 最后就是使用语言去解决问题,在解决的过程中学习如何去设计程序框架、划分模块、编写makefile、编写单元测试、编译、集成、部署、测试等工作。