辨析两组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)是一个接口,它定义了一个数据类型和对该类型的值所进行的操作。