专栏首页C/C++基础申明与定义的区别

申明与定义的区别

C++编码过程中,我们经常谈及“定义”和“声明”,二者是编程过程中的基本概念。我们需要使用一个变量、类型(类、结构体、枚举、共用体)或者函数时,我们需要提前定义和申明。定义和申明的过程,就像我们向图书馆借阅书籍一般,需要先完成书籍的印刷,即创造出书籍,这是一个定义的过程,有了书籍,我们需要到图书馆完成借阅的登记手续,这是申明的过程。完成了申明,我们有了使用书籍的权限,就可以尽情的畅游在知识的海洋。如果说书籍是自己委托印刷厂印刷的,那么你无需向他人借阅,即无需声明,可以直接使用书籍。一本书籍只需要印刷一次,但是可以被多人多次借阅,也就是说定义只需要一次,但是申明可以有多次。这里的书籍指代的是“定义”和“声明”作用的对象,即变量、类型和函数。C/C++中,使用一个变量、类型或者函数必须先在使用前完成定义和申明。

定义和申明是容易混淆的概念,但通过上面的类比说明,可以看出: (1)“定义”和“申明”的本质区别是声明可以出现多次,而定义只能出现一次; (2)把申明的东西放在头文件,把定义的东西放在源文件(.c或者.cpp文件); (3)类型的定义应该放在头文件,因为类型不具有外部连接性,不同源文件拥有相同的类型定义不会报编译错误,但头文件不能重复包含。

1.变量的定义与申明

定义变量,指明变量所属类型、名称、分配内存空间与初始化其初始值,如

int a=1;
int a(1);

如果不显示初始化,则按照编译器默认进行初始化。

申明变量,指明变量所属类型与变量名称,如:

extern int a;

有一点需要注意,对于局部变量以及全局静态变量是不能通过extern进行前置申明的,即不能在定义之前通过申明来引用,因为局部变量的作用域是当前代码块,全局静态变量的作用域是当前源文件,都不是全局作用域,所以不能通过extern进行前置申明。全局变量允许在定义之前通过前置申明进行引用。参见如下代码片段。

#include <stdio.h>

extern int a;
extern static int b;    //报错
int main()
{
    extern int c;       //报错
    printf("a=%d,b=%d,c=%d",a,b,c);
    int c=2;
}
int a=0;
static int b=1;

2.类型的定义与申明

定义类型,指明类型的名称和内容。

struct test{int a;}

或者给已经存在的类型起个别名。

typedef int int32;

注意,类型的作用域是源文件,即类型不具有外部连接性质,因此可以在不同源文件定义相同名称的类型,比如定义同名的类是不会报重定义错误的,这也说明了类型的定义应该放在头文件中,但在同一个源文件定义相同名称的类型则编译器会报重定义错误。

声明类型,只给出类型的名称。

class A;

类型被声明之后,可以用于声明其他的标识符,但不能利用它来定义对象,也不能使用类型的成员。

class A;            //先声明类
void show(A& a);    //申明函数
class A             //后定义
{
public:
    int a;
    char b;
};

以上代码没有错误。但如下代码将报错:

class A;    //先声明
int main(int argc,char* argv[])
{
    A classA;
    classA.a=5;
    return 0;
}

class A     //后定义
{
public:
    int a;
    char b;
};

该程序无法通过编译,原因是类A的所有成员的有效范围均是从定义类的地方开始,故使用未知的构造函数初始化其成员变量是错误的,会报使用未定义的 class “A”错误。

使用类型之前给出其定义式即可,即给出类型的名称和内容,或者利用typedef给一个类型起个别称。虽然说是定义类型,但还是将类型的定义放在头文件,即使被不同的源文件包含,因为类型不具有外部连接特性,所以不会报重定义错误的,这和变量和函数不同,不能将变量和函数的定义放在头文件!切记!这里所说的外部连接与内部连接的区别在于链接器接的时候是否将当前目标文件中的定义与其它目标文件对比,并报告是否有重定义错误。内连接不会进行对比,故不同源文件中定义相同名称的类型是不会报错的。

3.函数的定义与申明

定义函数,指明函数返回类型、函数名称、函数参数和函数体。

int test(char a,int b)
{
    return a+b
}

申明函数,指明函数返回类型,函数名称和函数参数。

int test(char a,int b);
//或者无需给出形参名称,只需要类型即可
int test(char,int);

从上面可以看出,函数定义与函数申明的区别是主要有两点: (1)函数定义需要给出函数体,即函数的具体实现,函数声明不需要; (2)函数定义必须给出形参名称,申明可以只给出形参类型。


参考文献: [1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++类的设计与实现规范

    规范是一种规定,遵守这种规定能够带来长远的利益,而违反这种规定却不会立即收到惩罚。程序设计的规范是人们在长期的编程时间中总结出来的,深入理解中这些规范需要认真的...

    Dabelv
  • C++11新关键字

    auto是旧关键字,在C++11之前,auto用来声明自动变量,表明变量存储在栈,很少使用。在C++11中被赋予了新的含义和作用,用于类型推断。

    Dabelv
  • 数组中的逆序对

    题目: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

    Dabelv
  • Xcelsius系列的1——初识动态仪表盘

    今天是Xcelsius系列的第一篇——初识动态仪表盘。 该案例将会讲解一个简单的电信公司月度业务分析数据模型,通过本案例你可以简单的了解水晶易表中的图表部件(柱...

    数据小磨坊
  • 操作系统-位示图实验

    柴银磊
  • jsp权限菜单绑定的基本实现

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    逝兮诚
  • Java工具集-线程工厂类

    cwl_java
  • Java底层-Javac编译器

    当我们执行这个命令后,就会得到一个class文件,这一步其实就是编译,在前面一节我们提及到Java不同于C/C++,它首先需要将Java文件编译成class文件...

    每天学Java
  • 使用云函数快速打造公众号自动回复机器人

    想法 于上周五发现了一个有趣的仓库: co-wechat(https://github.com/node-webot/co-wechat)。突发奇想,我不要你...

    腾讯云serverless团队
  • centos7 安装cmake

    旺财的城堡

扫码关注云+社区

领取腾讯云代金券