C语言中静态、动态属性
编程语言分为静态语言和动态语言,静态语言在编译时进行类型检查,不允许在运行时修改变量类型,通常具有更严格的语法和较慢的开发速度,但在运行时具有更高的效率,常见的静态语言有C、C++、Java、Rust等;而动态语言的类型检查发生在运行时,因此具有较为宽松的语法规则和更快的开发速度,但这牺牲了程序的运行速度,常见的动态语言有Python、JavaScript、Ruby等。
那么C语言中有哪些静态/动态的术语呢?以下简述C语言的部分动态/静态属性:
静态依赖 vs. 动态依赖
在程序的世界里,依赖通常指的是一个软件组件(如库、框架、模块或软件包)依赖于另一个组件才能正常工作的情况,通过静态、动态两个属性又可以分为静态依赖、动态依赖。静态依赖 通常指程序在编译时用到的一些资源,比如头文件、接口定义等;而动态依赖是程序执行期间需要记载的依赖,通常是另一个组件的库、框架,一般用来提高软件的灵活性和适应性,如Win下的.dll
和Linux下的.so
文件。
静态调用 vs. 动态调用
正常的函数调用既是一种静态调用,相比于正常的函数调用,当一个函数作为参数去实现另一个函数的功能时(称为回调函数),此时函数指针可以动态地指向不同的函数,允许在程序运行时更改函数,因此这种调用称为动态调用。
静态多态 vs. 动态多态
静态(编译时)多态
由编译器解释的多态为静态多态,这里介绍下重载解析和模板解析。首先说一下重载解析的工作,在这个过程中,编译器会首先识别出函数名称相同但是参数列表不同的重载函数,接下来编译器会根据参数列表匹配最合适的函数,然后进行类型检查,最后所有工作完成后,来实现函数的调用。然后是函数的模板解析,当遇到模板调用(例如std::vector<int>
)时,编译器首先创建该模板的一个具体实例,然后推断出模板参数的类型(例如int
在std::vector<int>
中),完成后模板的参数被替换为推导出的类型。
以上两种方式都实现了函数的静态多态
动态(运行时)多态
动态多态主要是虚函数的实现,对于每个派生类,编译器都会创建一个虚表(虚表是一个包含虚函数实现的指针的表,每个对象的第一个成员都是一个指向其虚表的指针),当调用虚函数时,编译器会使用对象的虚函数指针查找虚表中的正确函数指针,然后再调用该函数。
静态内存分配 vs.动态内存分配
静态内存分配在编译时,编译器便为变量分配好了大小和位置,其中初始值为零的全局变量和静态变量放在BSS
段、初始值非零的全局变量和静态变量放在Data
段,代码放在Code
段,函数形参和局部变量放在栈区;动态内存分配发生在运行阶段,需要使用 malloc
、calloc
、realloc
等函数从堆中分配内存,其分配的内存大小在程序运行时才能够确定。分配情况如下图:
总结
C语言的静态、动态属性贯穿学习C语言的整个时期,从基本语法、面向对象一些特性到内存管理的底层机制,能把这个属性弄清楚是非常具有挑战性事情,希望以上内容能对你有所帮助