编译
GNU编译器
推荐使用 GNU 编译器。
手动
- MinGW distro下载并安装。
scoop
我比较推荐使用scoop安装和下载。
scoop install main/mingw
命令行编译
熟练之后也有玩家会使用更灵活的命令行来编译代码,这样就不依赖 IDE 了,而是使用自己熟悉的文本编辑器编写代码。
g++ main.cpp -o test -lm
g++ 是 C++ 语言的编译器(C 语言的编译器为 gcc),-o 用于指定可执行文件的文件名,编译选项 -lm 用于链接数学库 libm,从而使得使用 math.h 的代码可以正常编译运行。
注:C++ 程序不需要 -lm 即可正常编译运行。历年 NOI/NOIP 试题的 C++ 编译选项中都带着 -lm,故这里也一并加上。
-x language filename 比如gcc -x c hello.pig 设定文件所使用的语言,使后缀名无效,对以后的多个有效
# 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面.
g++ -E main.cpp -o res.i
# 慢慢看吧,一个hello word 也要处理成800行的代码
# 只激活预处理和编译,就是指把文件编译成为汇编代码。
g++ -S main.cpp
# 他将生成.s的汇编代码,你可以用文本编辑器察看
# 只激活预处理,编译,和汇编,也就是他只把程序做成obj文件
# 他将生成.o的obj文件
g++ -c main.cpp
相关语法
include
#include 其实是一个预处理命令,意思为将一个文件「放」在这条语句处,被「放」的文件被称为头文件。也就是说,在编译时,编译器会「复制」头文件 iostream 中的内容,「粘贴」到 #include <iostream> 这条语句处。这样,你就可以使用 iostream 中提供的 std::cin、std::cout、std::endl 等对象了。
如果你学过 C 语言,你会发现目前我们接触的 C++ 中的头文件一般都不带 .h 后缀,而那些 C 语言中的头文件 xx.h 都变成了 cxx,如 stdio.h 变成了 cstdio。因为 C++ 为了和 C 保持兼容,都直接使用了 C 语言中的头文件,为了区分 C++ 的头文件和 C 的头文件,使用了 c 前缀。
一般来说,应当根据你需要编写的 C++ 程序的需要来确定你要 #include 哪些头文件。但如果你 #include 了多余的头文件,只会增加编译时间,几乎不会对运行时间造成影响。目前我们只接触到了 iostream 和 cstdio 两个头文件,如果你只需要 scanf 和 printf,就可以不用 #include <iostream>。
可以 #include 自己写的头文件吗?答案是,可以。
你可以自己写一个头文件,如:myheader.h。然后,将其放到和你的代码相同的目录里,再 #include "myheader.h" 即可。需要注意的是,自定义的头文件需要使用引号而非尖括号。当然,你也可以使用编译命令 -I <header_file_path> 来告诉编译器在哪找头文件,就不需要将头文件放到和代码相同的目录里了。
main()
可以理解为程序运行时就会执行 main() 中的代码。
实际上,main 函数是由系统或外部程序调用的。如,你在命令行中调用了你的程序,也就是调用了你程序中的 main 函数(在此之前先完成了全局 变量 的构造)。
最后的 return 0; 表示程序运行成功。默认情况下,程序结束时返回 0 表示一切正常,否则返回值表示错误代码(在 Windows 下这个错误代码的十六进制可以通过 Windows Error Codes 网站 进行查询)。这个值返回给谁呢?其实就是调用你写的程序的系统或外部程序,它会在你的程序结束时接收到这个返回值。如果不写 return 语句的话,程序正常结束默认返回值也是 0。
在 C 或 C++ 中,程序的返回值不为 0 会导致运行时错误(RE)。
注释
在 C++ 代码中,注释有两种写法:
- 行内注释:以 // 开头,行内位于其后的内容全部为注释。
- 注释块:以 /* 开头,*/ 结尾,中间的内容全部为注释,可以跨行。
注释对程序运行没有影响,可以用来解释程序的意思,还可以在让某段代码不执行(但是依然保留在源文件里)。
在工程开发中,注释可以便于日后维护、他人阅读。
在 OI 中,很少有人写许多注释,但注释可以便于在写代码的时候理清思路,或者便于日后复习。而且,如果要写题解、教程的话,适量的注释可以便于读者阅读,理解代码的意图。希望各位同学能养成写注释的好习惯。
C++ 中的空白字符 在 C++ 中,所有空白字符(空格、制表符、换行),多个或是单个,都被视作是一样的。(当然,引号中视作字符串的一部分的不算。)
因此,你可以自由地使用任何代码风格(除了行内注释、字符串字面量与预处理命令必须在单行内),例如:
#include <iostream>
int
main(){
int//x, y; std::cin
>> x >>y;
std::cout <<
y <<std::endl
<< x
;
return 0; };
当然,这么做是不被推荐的。
#define
是一种预处理命令,用于定义宏,本质上是文本替换。例如:
#include <iostream>
#define n 233
// n 不是变量,而是编译器会将代码中所有 n 文本替换为 233,但是作为标识符一部分的
// n 的就不会被替换,如 fn 不会被替换成 f233,同样,字符串内的也不会被替换
int main() {
std::cout << n; // 输出 233
return 0;
}
宏可以带参数,带参数的宏可以像函数一样使用:
#include <iostream>
#define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))
int main() {
std::cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << std::endl; // 输出 3 16
}
带参数的宏和函数是有区别的
#include <iostream>
#define sum(x, y) x + y
// 这里应当为 #define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))
int main() {
std::cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << std::endl;
// 输出为 3 11,因为 #define 是文本替换,后面的语句被替换为了 2 * 3 + 5
int i = 1;
std::cout << square(++i) << ' ' << i;
// 输出未定义,因为 ++i 被执行了两遍
// 而同一个语句中多次修改同一个变量是未定义行为(有例外)
}
使用 #define 是有风险的(由于 #define 作用域是整个程序,因此可能导致文本被意外地替换,需要使用 #undef 及时取消定义),因此应谨慎使用。较为推荐的做法是:使用 const 限定符声明常量,使用函数代替宏。
但是,在 OI 中,#define 依然有用武之处(以下两种是不被推荐的用法,会降低代码的规范性):
#define int long long+signed main()。通常用于避免忘记开 long long 导致的错误,或是调试时排除忘开 long long 导致错误的可能性。(也可能导致增大常数甚至 TLE,或者因为爆空间而 MLE)
#define For(i, l, r) for (int i = (l); i <= (r); ++i)、#define pb push_back、#define mid ((l + r) / 2),用于减短代码长度。
不过,#define 也有优点,比如结合 #ifdef 等预处理指令有奇效,比如:
#ifdef LINUX
// code for linux
#else
// code for other OS
#endif
可以在编译的时候通过 -DLINUX 来控制编译出的代码,而无需修改源文件。这还有一个优点:通过 -DLINUX 编译出的可执行文件里并没有其他操作系统的代码,那些代码在预处理的时候就已经被删除了。
#define 还能使用 #、## 运算符,极大地方便调试。