辨析两组C语言概念

一、声明和定义

本处参考《C专家编程》[美]Peter van der Linden

记住,C语言中的对象必须有且只有一个定义, 但它可以有多个exterm声明
Tips:顺便说一下,这里所说的对象跟C++中的对象并无关系,这里的对象只是跟链接器有关的“东西”,比如函数和变量

1)声明简单地说明了在其他地方创建的对象的名字,它允许你使用这个名字。
2)定义是一种特殊的声明,它创建了一个对象;

声明的简单应用:比如在C++中

//类B的声明,只是告诉编译器,我后面有定义这个类B,但是编译到这,暂时编译器不会给类B分配内存

class B;//前置声明

//类A的定义,编译器编译到这要给类A分配内存
class A
{

    public:
    A(int a):num(a)
    {

    }
    void test()
    {
        cout<<num<<endl;
    } 

    private:

    //要是class B的前置声明,则使用不了下面的 B * _b;
    B * _b; //若是定义该类型对象 B _b;则不行 

    int num;

   /*PS:前置声明的类B只是一个不完全的类型,
    *具体原因是:编译器还未给类B分配内存
    *所以,我们只能用完全类型只能以有限方式使用,不能定义该类型的对象,
    *不完全类型只能用于定义指向该类型的指针及引用,或
    *者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数
    *Reason:指针,我可以直接分配内存大小,而C++引用的底层也是指针实现的,也可以分配
    *但是,定义对象,那要借助类B这个模具去生产一个对象,但是现在那个定义的模具分配多少内存
    *至此暂时,不晓得,所以,不能用定义对象。
    */ 
};

//类B的定义,编译器编译到这才给类B分配内存
class B
{

    public:

    B(int c): _c(c)
    {

    }
    void demo()
    {
        cout<<_c<<endl; 
    } 

    private:
        int _c; 

};

具体的,本处,只是举个例子,想了解C++中前置声明的,可以去看下面两篇博客
https://blog.csdn.net/u010608296/article/details/102393543
https://www.cnblogs.com/lit10050528/p/3898835.html

让我们回顾一下这两个术语:

  • 定义:
  • 1)只能出现在一个地方
  • 2)确定对象的类型分配内存,用于创建新的对象。
  • 3)例如:int my_array[100];
  • 总的来说:定义相当于特殊的声明:它为对象分配内存。
  • 声明
  • 1)可以多次出现
  • 2)描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里)
  • 3)例如:extern int my_ array[];//至于此处,为什么数字的长度信息可以省略,原因见后面
  • 总的来说:声明相当于普通的声明:它所说明的并非自身,而是描述其他地方的创建的对象。

Reason:
1)exterm对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。
2)由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。
3)对于多维数组,需要提供除最左边一维之外其他维的长度——这就给编译器足够的信息产生相应的代码。

1)变量的声明和定义

变量的声明

声明变量的类型和名字,但不分配存储空间。
定义也是声明(准确来说是“特殊的声明”,严格说,定义就是定义,这是许多人搞不清楚的地方)。

举例子:

int a;
//准确说是定义,因为他它会分配内存,但是由于是“特殊的声明”
//也有人混用说明是“声明”,额,见怪不怪、、、大家对一些东西的说法有出入,自行判断吧。

extern int a;//纯粹的声明,毕竟不在这分配内存
class B;//纯粹的声明,不在这分配内存,这种写法是C++中的

变量的定义

指明变量的类型和名字,为变量分配存储空间。
另外补充一个概念变量的初始化,是指在变量的定义出给出值

举例:

int a;//纯粹的定义
int a=3;//是定义,并且,由于在定义的时候你主动给出了值,还可以更加准确的叫“变量初始化”

2)函数的声明和定义

简单的分辨:

.h头文件中的
//方法的声明
void get(char *s);
.c文件中的
//方法的定义
void get(char *s)
{
  ...
  return;
}
  • 最终补充,翻译的时候的一些事情:
    定义:DEFINE
    声明,有时也作说明:DECLARE //所以,翻译的人,要是统一了术语多好。。。

二、接口和实现

项目的开发过程中,我们需要使用到很多的功能模块
由于C语言的灵活性和自由性,若在编程时不遵循模块化编程思想,其代码的可读性将是一件令人头痛的问题。
为了提高项目工程的可读性和可维护性,我们将采用模块化编程思想,

模块分为两个部分,即模块的接口实现

简单的分辨:

  • 在C语言中
  • 接口在xxx.h
void get(char *s);
  • 1)预处理指令#include导入接口。
  • 2)其实是为了在进行模块化编程的时候,你写的模块要调用别人哪个模块,那你直接看接口就会比看具体实现代码简洁,并且这样,别人也能不用给你实现源代码,更好的分工。此外,这还降低了模块之间的耦合性。
  • 实现在xxx.c
void get(char *s)
{
  ...
  return;
}
  • 1)一个接口可以有多个实现。只要实现遵循接口的规定,完全可以在不影响应用程序的情况下改变实现。所以,我们现实生活中,某个公司需要一些公司竞标做模块,才成为了可能!
  • 2)在C语言中,一个实现通过一个或多个.c文件来提供。实现必须提供其导出的接口规定的功能,实现会包含接口的.h文件,以确保其定义与接口的声明一致。

最后的Tips:
在面向对象的语言中,比如C++和Java
一个抽象数据类型(ADT)是一个接口,它定义了一个数据类型和对该类型的值所进行的操作。