c 语言中的封装

谈及封装,大多数人首先会想到面向对象设计。封装其实是一个泛型的概念,虽然在面向对象设计中更容易体现出来,却也能够在面向过程的语言中找到应用领域。在本文中,我浅谈下 c 语言中涉及到的封装概念。

对过程的封装

1. 函数

函数有参数、执行块、返回值等信息。这些信息描述了一个功能实现的各个抽象方面。参数表示过程执行的依赖条件,返回值表示过程执行产生的结果。这样的描述对于纯函数完全适合。

所谓纯函数指的就是那些返回结果只依赖参数且没有副作用的函数。对于具有副作用的函数而言,这一描述不算特别恰当。实际 c 语言编程中我们在大多数时候都依赖函数的副作用来完成工作,使用纯函数编程的方法多见于函数式语言中,如 lisp 、scheme 等。

c 语言中的函数是对过程的封装,这一封装过程可以不断地向上发展。我们从最初的小函数开始封装基础的功能,然后这些函数又成为更高一层调用函数的封装元素。

这样一层层的封装下去,我们将得到一个层次较为分明的系统。每一层做的具体过程都封装到了许多函数中,各层之间通过固定的接口传递信息,这样当需要修改时大部分时间内只修改本层的函数即可完成任务,其它层的函数可以完全不用修改!

2. block

语句块是对多个执行语句的封装。在 c 语言中语句一定归属于一个 block 中。抛开参数与返回值不谈,函数其实也就是一个、多个执行块。不过这里说的执行块并不需要像函数一样的参数与返回值,语句块中的语句执行所产生的副作用却可能与函数执行相同。

语句块可以嵌套。每一级语句中定义的局部变量可以看做本语句块的内部属性封装,子语句块中的重名局部变量将覆盖父语句块中的变量。

对数据结构的封装

结构体

c 语言中的复合数据结构——结构体提供了对数据结构进行封装的方法。结构体与面向对象语言中的类颇有相同之处,只是更为简单。

结构体的元素既可以是基本数据类型,也可以是复合数据类型,这点无疑极大的增强了结构体的封装能力。

c 语言中的结构体并不存在类似面向对象语言的类中对属性进行访问控制的方法,它更接近数据结构本身。通过对不同类型的数据进行封装,我们完成了类似设计类的工作。封装得到的数据结构不仅能够描绘出对象的各种属性,也限定了基于此数据结构的操作。你也可已将对此数据结构操作的函数以虚函数表的形式封装到结构体中,这样结构体便有了类似面向对象类中的成员与方法,这也是使用 c 语言实现面向对象的常见操作。

对作用域的封装

我们之前在描述 block 中的封装时提到了每一个 block 中定义的局部变量仅对当前 block 中的语句可见,子 block 中的重名局部变量将覆盖父 block 中的局部变量。这便是一种对作用域的封装。这样的封装方式限定了变量的可见范围,降低了在不同地方修改变量导致问题的概率。

c 语言中可以使用全局变量。全局变量具有全局作用域,常常会因为不同地方的多个修改而造成问题。c 语言中提供了 static 关键字来修饰变量。实际上这一关键字既可以用于变量也可用于函数。用于变量时既表示对变量的作用范围进行封装,也表示变量的存储类型为静态类型。用于函数时则仅仅表示对函数的作用范围进行封装。

static 关键字用于变量

static 关键字修饰变量时,变量的作用范围最大为当前 .c 文件,最小则是在一个 block 中,后一种使用方式并不常见。

在一个 block 中定义一个 static 类型的变量会一直占用空间,不像栈中存储的变量,可以随着栈的增减而动态创建回收。

static 关键字用于函数

static 关键字修饰函数时,函数的作用范围仅限于当前的 .c 文件。c 语言并不支持函数的嵌套定义,因此此作用范围的大小是固定的。一般在开发中,所有不需要对外部提供的函数最好都定义为 static 类型,这样此函数名仅仅作用与当前的源文件,我们就可以在其它的源文件中定义相同名字的函数而不用担心编译器报重复定义的错误了。

总结

本文描述了 c 语言中封装的三种应用——对过程的封装、对数据结构的封装、对作用域的封装。通过对这三种类别封装的描述,我们可以看到封装不仅仅是面向对象语言中常见的特性,在面向过程语言中也有较多的应用。应该说封装是一种思想而非具体的技术,这样我们便能够在更广阔的层次去解释封装,对语言的认识也能更进一步!

    原文作者:longyu_wlz
    原文地址: https://blog.csdn.net/Longyu_wlz/article/details/95763602
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞