声明是告知编译器某实体存在及类型,定义则分配内存或提供实现;变量、函数、类等的声明与定义需严格区分,否则引发编译警告、链接失败或运行时错误。

声明是告诉编译器“这个东西存在,类型是啥”,定义是真正让它落地——分配内存或提供实现。不区分清楚,轻则编译警告、链接失败,重则多个 .o 文件里悄悄生成重复变量,运行时值错乱却查不出。
变量:int a; 是定义,extern int a; 是声明
只要没有 extern 修饰,且出现在文件作用域(非函数内),int a; 就是定义:编译器会给它分配内存(全局区清零,局部未初始化则为垃圾值)。而 extern int a; 只是说“别急着报错,a 在别的 .cpp 里定义了”,不占内存、不初始化。
-
extern int a = 42;看似带extern,但因有初始化式,仍是定义——extern此时只表示“外部链接”,不是声明 - 在函数内部写
extern int a;合法,但几乎没用:它只能引用已定义的全局a,不能用于局部变量前置声明 - 静态局部变量(如
static int s;)是定义,但具有内部链接,其他文件看不见
函数:void f(); 是声明,void f() { } 是定义
函数声明只需类型和签名,让编译器能检查调用是否合法;函数定义必须含函数体({}),否则链接时报 undefined reference to 'f'。即使函数声明写在头文件里,也绝不能把 void f() { ... } 放进去——否则每个包含它的 .cpp 都会生成一份定义,违反 ODR(单定义规则)。
- 内联函数(
inline void f() { })例外:定义可多次出现,但所有定义必须完全一致 - 类内定义的成员函数(如
struct S { void m() { } };)默认是inline,所以算定义但不违反 ODR - 纯虚函数声明(
virtual void f() = 0;)只是声明,不提供实现,也不分配内存
类和结构体:struct S; 是声明,struct S { int x; }; 是定义
struct S; 告诉编译器“S 是个类型名”,但此时它是不完整类型(incomplete type):不能 sizeof(S),不能定义 S 类型变量,也不能访问其成员。只有看到完整定义(含大括号和成员列表),编译器才知大小、布局和接口。
立即学习“C++免费学习笔记(深入)”;
- 类定义本身是声明 + 定义:它引入名字(声明),又描述内存布局(定义)
- 类中声明的静态数据成员(如
static int count;)只是声明;必须在类外(.cpp中)用int S::count;或int S::count = 0;才算定义 - 模板声明(
template<typename t> class C;</typename>)是声明;模板定义(含{})才是定义,但模板定义通常也得放在头文件里,因为实例化发生在使用点
最容易被忽略的是:头文件里混入变量定义(比如误写 int global_flag = 0;),会导致每个包含它的源文件都生成一个独立的 global_flag,链接时可能静默选一个,也可能报多重定义——而错误信息往往指向链接器,让人摸不着头脑。


















