📄 一句话概括
声明是"先打个招呼":只写函数头、末尾加一个分号,告诉编译器"有这么个函数、它怎么用",真正的函数体(定义)可以晚点再出场。声明专治"想先用、后写定义"的顺序问题——编译器读代码是自上而下一遍过的,用到之前它必须"认识"这个名字。
📝 本节和前面几节的关系
6.1.1 学的是把机器造出来(定义),这一节解决顺序问题。为什么会有顺序问题?回看 2.1.4:程序执行从 main 第一条语句开始,但编译器读文件是从第一行开始的。读到 add(3, 5) 那一刻,它必须已经知道"add 收两个 int、还一个 int"。声明就是把这份"用法说明"提前摆出来。
一、编译器为什么需要声明
编译器像一个从上往下读、且只读一遍的读者:
- 读到
add(3, 5)时,它得立刻核对:"有 add 这个东西吗?它收几个参数?参数类型对得上吗?返回值拿来干什么合不合法?" - 如果此刻还没见过 add 的任何信息 → 报错
'add' was not declared in this scope(add 未在此作用域声明)。
两种解法:要么把定义放在调用前面,要么先写一句声明。声明的好处是——函数体可以挪到文件后面(甚至别的文件里),main 照样能放在最前面读。
二、声明长什么样:定义砍掉函数体,留个分号
int add(int a, int b); // 声明:函数头 + 分号(参数名可省)
int add(int a, int b) { // 定义:函数头 + 函数体
return a + b;
}
一句口诀记牢:分号 = 声明,花括号 = 定义。
三、实例 01:声明、定义和使用函数
#include <iostream>
using namespace std;
int add(int a, int b); // ① 声明:提前打招呼
int main() {
cout << "3 + 5 = " << add(3, 5) << endl; // ② 使用:放心调用
cout << "10 + 20 = " << add(10, 20) << endl;
return 0;
}
int add(int a, int b) { // ③ 定义:真身放在后面
return a + b;
}
看到什么算成功:
3 + 5 = 8
10 + 20 = 30
必做实验(本节的关键一步):把第 4 行那句声明注释掉,重新编译——
error: 'add' was not declared in this scope
报错信息就是答案:not declared = 缺声明。记住这张脸,以后见到它就知道该补声明(或把定义往前挪)。
再试一次:把 int add(int a, int b) { ... } 整段定义剪切到 main 之前,声明也删掉——照样能编过。这证明:编译器要的是"用之前先认识",声明和定义都能满足它,只是声明更灵活。
四、声明与定义的分工
| 声明(原型) | 定义 | |
|---|---|---|
| 写什么 | 函数头 + ; |
函数头 + { 语句 } |
| 对编译器说 | 怎么用(名字/参数/返回类型) | 怎么干(具体语句) |
| 能出现几次 | 可以多次(内容必须一致) | 有且只有一次 |
| 在内存里有位置吗 | 没有 | 有 |
一句话:声明是菜单,定义是后厨。顾客(main)看菜单就能点菜,菜在后厨现做——不用让顾客先进后厨看一眼才能点。
五、常见误区
- "声明和定义差不多,随便写哪个"——差一个分号和一对花括号,但作用完全不同:声明不产生代码,定义才产生代码。
- "定义也能写多次"——重复定义会报错(redefinition / multiple definition)。而声明写多次(内容一致)是允许的,这正是头文件能被到处
#include的原因(1.3.1 讲过多文件工程,后面会重逢)。 - "声明里的参数名必须和定义一样"——不必,声明里参数名甚至可以全省略:
int add(int, int);。参数名只在函数体里有用。 - "有声明兜底,函数体写错也会被揪出来"——不一定。编译器只按声明检查调用,函数体里的问题要等链接阶段或运行时才暴露(回看 1.3.1 的"编译 + 链接"两步)。
30 秒实验:把声明改成 double add(int a, int b);(故意和定义不一致),编译看报错——声明和定义必须口径一致,这就是原型的主要价值:它是编译器手中的"合同"。
自测三问(点开对答案)
1. 声明和定义在写法上差什么?——声明只有函数头 + 分号;定义是函数头 + 花括号里的函数体。 2. 为什么需要声明?——编译器自上而下只读一遍,调用前必须先"认识"这个名字(或在调用前就写定义)。 3. `'xxx' was not declared in this scope` 通常是什么原因?——调用前既无声明也无定义(漏写声明,或定义位置在调用之后)。- ☐ 6.2 函数参数及返回值(下一节)
相关笔记
- 函数的定义 —— 定义的四个部件
- main 函数 —— 执行从 main 起、编译从第一行起
- C++工程项目文件 —— 编译与链接两步分工
- 🔷 C++基础 · 索引 —— 全课路线图