算是C嘎嘎入门教程(但至少需要知道HelloWorld怎么写
内容不能说全,因为是想到哪写到哪,再次就是C++是真的很杂。
(写完后博文编辑器提示我,本文章可能要20分钟读完,做好心理准备…
1、头文件(*.h)、源文件(*.cpp)
- 头文件不参与编译,头文件的最大作用是简化/规范代码。一大段代码可以移动到“XXX.h”中,然后那大段代码的位置可以简单一个
#include"XXX.h"
代替。通常情况下头文件仅可包含函数声明、变量声明、类/结构体的声明或定义。 - 源文件参与编译,无法通过编译那一般是某名称未声明/未定义(或者过定义/重复定义),一般IDE(例如VSCode)能给你指出这些语义错误。
- 绝对不能
#include"XXX.cpp"
,哪怕目前能跑,下一步指不定瘸给你看。迫不及待想看是怎么瘸的?跳转到4.2,但还是建议顺序阅读(毕竟文章内容我进行过调整的
2、声明与定义
- 声明可以重复无数次,但定义只能一次(不能多也不能少),无论是变量、函数、类、结构体。
- C++的定义具有声明效果。
- 如果变量有初始化那么肯定是定义,不可能是声明。例如
extern int num;
和extern int num=3;
,前者是声明,后者是定义;也例如struct Item;
和struct Item{int num=5;};
,前者是声明,后者妥妥的是定义。 - 仅有声明而没有定义的类/结构体是无法实例化的(也就是无法创建对象),会在编译阶段出现报错。
- 仅有声明而没有定义(或未能寻找到定义)的变量/函数,会在链接阶段出现报错。
2.1、以下表格展示声明和定义的区别:
说明 | 代码截图 |
---|---|
裸奔(不带static /extern 修饰)的是定义,并且会被赋予默认初值 |
![]() |
有static 修饰的变量是定义 |
![]() |
有extern 修饰的变量是声明。但如果对变量赋初值那么为是定义(因为初始化) |
![]() |
声明/创建类型:class /struct |
![]() |
创建类型:typedef |
![]() |
声明/创建函数 | ![]() |
2.2、以下表格展示仅有声明没有定义时的报错:
说明 | 代码截图 |
---|---|
变量 | ![]() |
函数 | ![]() ![]() ![]() |
类/结构体 | ![]() |
3、static
和extern
这俩修饰词可以作用于变量和函数,而对类class
和结构体struct
来说将不起作用的,不起效的原因说来话长,这里展开会跑题。
3.1、static
和extern
的作用:
作用域 | extern |
static |
<无> |
---|---|---|---|
全局 | [定义]:变量/函数可供其他cpp文件访问 [声明]:可访问其他cpp文件的变量/函数 |
[定义]变量/函数仅供本cpp文件使用(可有效防止污染命名空间 [声明]仅用于函数,声明一个静态函数 |
(等同extern ) |
局部 | [声明]:访问全局变量/函数。实际没啥用处,但可以起一个规范作用(强调某东西是全局的 | [定义]:创建静态局部变量,创建行为只会执行一次并且变量会长久滞留内存中直到程序结束 | [定义]:创建局部变量,仅供本作用域使用 |
特别的,在类中使用static
修饰的静态成员变量仅仅是起到“声明”的效果,还要对该变量进行定义。因为这是类内的,在此不展开说明。类内搞特殊的语法多了去了,全指出来可是会死人的,除了static
之外还有virtual
、final
、explict
、operator
、const
、mutable
等关键词。
3.2、static
和extern
的应用:
多cpp文件下它们的作用尤为重要:
extern
确保函数/全局变量定义在指定cpp文件,防止出现重复定义;static
保护变量/函数不受其他cpp文件的干扰,同时也不污染全局命名空间(有效避免无意中造成的重定义);extern
用的比较多的情况下是全局声明(也就是放在函数体外),放在函数体内的局部声明更像是起到一种强调作用;extern
在声明/定义函数时可省略(有和没有都一个样)。
以下为样例代码和运行结果:
//T1_Main.cpp
extern int num;
extern void Func_1(int);
extern void Func_2(int);
int main() {
num = 100;
Func_1(num);
Func_2(num);
//extern void Func_0();//Func_0是T1_Extra-2.cpp下定义的静态函数,
//Func_0();//取消这两行注释后IDE会提示Func_0未定义(因为静态函数不对外
return 0;
}
//T1_Extra-1.cpp
#include<stdio.h>
int num = 10;
void Func_1(int val) {
printf_s("%d,%d\n",num,val);
}
//T1_Extra-2.cpp
#include<stdio.h>
static int num = 10;
extern void Func_1(int);
static void Func_1(int val) {
return;
}
void Func_2(int val) {
Func_1(777);//可以看到这里并没有调用外部文件的Func_1,而是本文件下的静态函数Func_1。说明本文件下的静态函数优先级更高
printf_s("%d,%d\n", num, val);
}
static void Func_0() {
//本文件下的静态函数不对外,也就是其他cpp文件是没法访问到这里的函数Func_0
printf_s("!!!\n");
}
4、#include
4.1、用简单但离谱的代码来展示#include
的作用:
//T2_Extra.h
printf_s("%d\n", 555);
//T2_Main.cpp
#include<stdio.h>
int main() {
#include "T2_Extra.h"
return 0;
}
这份代码等同于:
#include<stdio.h>
int main() {
printf_s("%d\n", 555);
return 0;
}
#include
的作用就是简单粗暴的文本替换罢了,没什么特别的。
4.2、#include
一个cpp文件的问题:
为什么我要放在这里才讲#include
的作用,那是因为只有这样才能清晰地表述#include"XXX.cpp"
这种行为到底有多蠢。
//T3_Main.cpp
#include<stdio.h>
#include"T3_Extra.cpp"
int main(