0、工具网站收集
C++语言版本特性
https://en.cppreference.com
https://www.cplusplus.com
https://www.tutorialspoint.com/cplusplus
https://www.learncpp.com
https://github.com/fffaraz/awesomecpp
https://stackoverflow.com
网页CPP编译器
[C++] gcc 12.1.0 - Wandbox
1.2、开始
P1、欢迎来到C++
C#、Java是在虚拟机上运行的,意味着代码首先编译成一种中间语言,当在目标平台上运行应用程序时,先运行虚拟机,再转换成机器码
C++直接操控硬件,C++编译器为目标平台和目标架构直接生成机器码
P2、环境安装(windows)
VS2017的安装教程
File->new project->Visual C++ ->General->Empty Project
然后起好名字、确定好路径就ok了
进入项目,在Solution Explorer中自己的Solution下的Source File->add->new Item
P5、C++是如何工作的
#符号后面都是预处理语句,编译器会首先处理(编译发生之前)
main函数比较特殊,不一定非要返回值(没写的话默认返回0)
#include<iostream>
int main() {
std::cout << "hello World!" << std::endl;
std::cin.get();
}
运行顺序:
-
编译器先处理“预处理语句”,即编译器先将iostream文件内容全部包含进来,可以理解为拷贝粘贴到自己的文件中
-
第二步,文件被编译,编译器将所有的C++代码转换为实际的机器代码。以VS配置为例:
- 解决方案平台(默认Debug 和 x86或win32)
- 配置只是构建项目的时候的一系列规则(右键解决方案->属性)
- 解决方案是指你编译的代码的目标平台
- x86的意思就目标平台为windows 32位,会生成32位的windows应用程序
- 目标平台也可以是Android,但要确保配置和平台的正确
- Debug和release的区别:默认Debug会更慢,release更追求速度。Debug关掉了很多优化以方便我们更好调试代码。
- 配置类型是生成文件的格式.exe .dll
- 包括C/C++下的配置,这些规则控制我们的文件如何被编译
- 在程序员的视角,编译只编译 .cpp文件,因为头文件被复制粘贴进 .cpp文件中了
- 每一个cpp文件都被编译成了一个object file(目标文件),vs生成的文件后缀是.obj
-
然后,链接(link)将生成的.obj文件合并成一个执行文件: How the C++ Linker Works
- 单独编译一个文件,不会发生链接
PS: error list仅供参考,一般信息不全,建议看output 窗口。
link的一个例子 (使用声明):
//main.cpp
#include<iostream>
void Log(const char* message);
int main() {
Log("hello World");
std::cin.get();
}
//log.cpp
#include<iostream>
void Log(const char* message) {
std::cout << message << std::endl;
}
P6、C++编译器是如何工作的
编译原理既视感
相比其他语言,在C++里,文件变得没有意义
- Java里主类命要与 .java 名字一致,包名的命名层级关系,文件可以被看做是组成单元
- 但在C++里,文件更像是一种协议或者是约定,.cpp被视为c++文件, .c 被视为c文件, .h被视为头文件,不同的文件对应的“约定”不同。
- 编译器只是将一个cpp文件变成一个翻译单元,一个翻译单元会生成一个.obj文件,实际上cpp文件可以包含其他的cpp文件,最后也是一个翻译单元
一个成功编译的例子:(#include的复制粘贴)
int Mutiply(int a, int b)
{
int result = a * b;
return result;
#include "EndBrace.h"
//EndBrace.h
}
-
在项目属性->C/C+±>Output Files->Assembler Output设置为/FA,编译之后就会生成一个.asm文件,内容为汇编指令。
-
在项目属性->C/C+±>Optimization设置为Maximum Speed并且把Code Generation->Basic Runtime Checks设置成Default,编译之后再看.asm文件,发现文件相比之前的设置小了许多,这是因为编译器对代码做了优化。
1.5、继续(巫师3真好玩)
P7、C++链接器是如何工作的
链接的主要工作:找到每个符号和每个函数在哪里,并把他们连接起来。即把所有的.obj文件链接在一起。
链接过程常见错误:(错误代码LNK)
- 未解决的外部符号unresolved external symbol : 连接器找不到他们需要的东西。
如果从来没有调用过某个函数(代码级),那么链接器不需要去链接这个函数。反之可能有例外:
static修饰的函数意味着这个函数只被声明在当前翻译单元中
//Main.cpp
//代码上Multiply函数中调用了Log函数,如果去链接Log函数将会出错
//但由于static的修饰,链接器判断Multiply函数将不可能被调用(本翻译单元内未调用+其他翻译单元不使用Mutiply)
//故编译和链接过程都不会出错。
//去掉static或者main中的注释//都会导致出错
#include<iostream>
void Log(const char* message);
static int Multiply(int a, int b){
Log("Multiply");
return a * b;
}
int main() {
//std::cout << Multiply(5, 8) << std::endl;
std::cin.get();
}
//Log.cpp
#include<iostream>
void LogR(const char* message) {
std::cout << message << std::endl;
}
- 静态链接
- 动态链接
P8、C++变量
核心思想,不同变量之间的区别是分配的空间大小。
P9、C++函数
没什么东西
P10、C++头文件
#pragma once在头文件中的作用:
- 告诉编译器这个头文件只被编译一次
- 尤其防止 .h 文件中出现结构体重复编译将会报错
另一种做法做法:(头文件保护符)
#ifndef _LOG_H
#define _LOG_H
void Log(const char* message);
void InitLog();
struct Player{};
#endif
#include 尖括号<>和引号""的区别:
- 如果要包含的文件在其中一个文件夹里,用尖括号告诉编译器搜索包含路径的文件夹
- 引号通常用于包含相对于当前文件的文件,但引号其实可以做一切。
P11、如何在Visual Studio中调试
在debug模式下,内存视图中值为 0xCC 的内存为未初始化的栈空间