[Cherno C++ 笔记 P1~P10]安装、链接器、变量、函数、头文件

系列博客

[Cherno C++ 笔记 P1~P10]安装、链接器、变量、函数、头文件
[Cherno C++ 笔记 P11~P20]判断,循环,指针,引用,类
[Cherno C++ 笔记 P21~P30]static,枚举,构造函数,析构函数,继承,虚函数,接口,可见性
[Cherno C++ 笔记 P31~P40]数组、字符串、CONST、mutable、成员初始化列表、三元操作符、创建初始化对象、new关键字、隐式转换与explicit
[Cherno C++ 笔记 P41~P50]运算符重载、this、生存期、智能指针、复制与拷贝构造函数、箭头操作符、动态数组、std::vector、静态链接、动态库

前言

这个系列的视频需要一些基础,最好是学过C。

视频链接

P1 欢迎来到C++
P2 Windows 上安装C++
P3 Mac上安装C++
P4 Linux上安装C++
P5 C++是如何工作的
P6 C++编译器是如何工作的
P7 C++链接器是如何工作的
P8 C++变量
P9 C++函数
P10 C++头文件

P1~P4 安装部分

安装部分网上也有很多,安装也简单。
现在又很多工具可以用来写C++,这里推荐使用vs。

P5 C++是如何工作的

第一个C++程序
#include<iostream>

int main() 
{
	std::cout << "Hello world!" << std::endl;
	std::cin.get();
}

#include<iostream> 叫做预处理语句编译器收到源文件后,会预先处理,找到<>中的文件,然后拷贝到这个位置。

main是程序的入口,计算机从这个函数开始执行代码。在这里,main函数的返回类型是int,但是代码中没有返回任何东西,这是因为main函数是特殊的,它不一定需要返回值。没有写return进行返回的话,它会默认返回了0。

<< 符号叫做重载运算符,可以理解为一个函数。

视频有很大一段在讲vs的相关设置,感兴趣的话可以观看视频。

代码如何编译为二进制文件

项目中的每一个cpp文件都会被编译,但是头文件不会被编译,头文件的内容在预处理时包含到了cpp中。
每一个cpp文件都被编译为object file(目标文件)vs生成的文件后缀为.obj,我们需要将这些文件合并成一个执行文件,链接(Link)会将所有的obj文件黏合在一起,合并成一个.exe文件。
编译生成的.obj文件
build整个项目生成的.exe文件

第一个函数
void Log(const char* message) {
	std::cout << message << std::endl;
}

int main() 
{
	std::cout << "Hello world!" << std::endl;
	Log("Hello World!");

	std::cin.get();
}
将函数放在其他文件中

在源文件中新建一个Log.cpp

#include<iostream>

void Log(const char* message) {
	std::cout << message << std::endl;
}

将Log函数放在其他地方后,main并不知道发生了这样的改变,它找不到叫做Log的函数,这时进行编译会报错。
我们需要在main所在的文件中声明Log函数存在

#include<iostream>

void Log(const char* message);

int main() 
{
	std::cout << "Hello world!" << std::endl;
	Log("Hello World!");

	std::cin.get();
}

声明:
声明表示这个符号、函数是存在的。void Log(const char* message);就是一个声明,它没有函数体。
定义:
定义是说明这个函数到底是什么,是函数的函数体。

在构建整个项目时,所有.cpp文件都会被编译,链接器会找到正确的Log函数的定义在哪里,如果找不到定义,会报链接错误。

P6 C++编译器是如何工作的

C++编译器实际上负责什么?

C++编译器只负责一件事,将文本文件(我们写的代码)转换成称为目标文件的中间格式。
这些obj文件可以传递到链接,链接可以做它所有要链接的事情。
编译器在生成这些obj时:

  1. 预处理代码。这意味着所有的预处理器语句都会先处理。
  2. 记号化和解析。将文本转换为编译器真正能够理解和推理的格式。这创建了所谓的抽象语法树。语法树一旦被创建,编译器就可以开始实际生成代码

与java不同的是,c++并不关心文件,文件只是提供给编译器源代码的一种方式。 可以随便创建一个文件(如xxx.hello_world),只要告诉编译器这是一个c++文件,就可以进行编译。
我们提供给编译器的每个c++文件,编译器都将把文件变成翻译单元(一个cpp文件并不一定对应一个翻译单元),翻译单元生成一个obj文件。

obj文件中都是机器码,我们可以在vs中查看汇编语句。

  1. 首先,我们在这里打一个断点在结束前打断点
  2. 进行调试进行调试
  3. 右键,转到反汇编反汇编
  4. 可以看到相应语句转换为的汇编语句汇编

可以在属性中开启优化、关闭优化, 对比一下生成的汇编语句

P7 C++链接器是如何工作的

什么是链接

链接时一个过程,当我们编译好源文件,我们需要通过一个叫做链接的过程,现在链接的主要任务时找到每个符号和函数所在的地方,并把它们链接起来。
多个翻译单元之间并不互通,我们需要一种方法把这些文件连接起来成一个项目。即使只有一个翻译单元,也需要将main函数连接起来,这就是链接器的主要目的和要做的事情。

链接错误
未解决的外部符号

当链接器找不到确切定义时发生。

我们写一个死代码,它没有被任何函数调用,其中Log函数虽然被定义但并不存在。在这里依然会报错。

虽然我们没有使用Multiply函数,但是在技术上来讲,我们有可能会用到这个函数,所以链接器需要链接到它,所以会报错。
无法解析的外部符号

我们使用static将Multiply函数限制在该文件中使用,由于我们没有使用该函数,所以不再报错。
加入static不再报错
在这里,我们并没有真正导入我们所写的返回void的Log,这里返回为int的函数由于没有明确的定义,所以会引发报错。没有明确定义导致的报错

有重复的符号

如果我们的函数或变量具有相同的名字或签名,链接器会不知道该链接哪一个。

在下面这个例子中,我们可以看到,即使没有申明,两个相同的函数放在不同的文件中,依然会报错。
即使放在两个文件中也会报错

P8 C++变量

P9 C++函数

P10 C++头文件

VS中的文件夹

vs中的文件夹
VS中的文件夹并不是真正的文件夹,你可以在资源管理器中看到并没有这些文件夹。这些文件夹实质上是过滤器。

第一个头文件

Log.cpp

#include<iostream>

void Log(const char* message) {
	std::cout << message << std::endl;
}

Log.h

#pragma once
void Log(const char* message);

Main.cpp

#include<iostream>
#include"Log.h"

int main() 
{
	
	Log("Hello world!");

	std::cin.get();
}

通过#include"Log.h"导入头文件,将头文件中的文本粘贴到该位置。

#pragma once

progma once: 只包括这个文件一次

#pragma once监督这个头文件,阻止我们单个头文件被多次包含,并转换为单个翻译单元。

这里我想用集合来举例说明:
我们现在有两个集合A{a,b,c,d}和B{c,d,e,f}(a,b,c,d,e,f均为.h文件),当我们同时需要调取A和B时,#pragma once的作用就是将A和B取并集为{a,b,c,d,e,f}而不是简单的合并成{a,b,c,d,c,d,e,f}。

可是使用另一种方法实现相同的功能

#ifndef _LOG_H
#define _LOG_H

void Log(const char* message);

#endif

#ifndef检查看是否有一个叫做_LOG_H的符号被定义了,如果没有被定义,则继续,如果被定义,则禁止。

“” 和 <>

在编译程序时,<>和""表示两种不同含义。

“”用来包含相对于当前文件的文件

<>用来搜索包含路径中的文件

“”也具有<>的功能

[未完待续]

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值