目录
例子说明C语言标准库和嵌入式C语言标准库是有区别的
我们从一个重定向的例子引入,一是在Linux GCC环境下,另一个是使用HAL库开发stm32串口应用。
在Linux GCC环境下,当我们要将标准输出(printf)重定向到其他文件的时候,是先将标准输入的文件描述符关闭,即关闭0号文件描述符,打开需要重定向的文件,进程就会将0号文件描述符分配给这个文件,标准输出默认会向0号文件描述符写内容。
使用HAL库开发stm32串口应用的时候,当我们想要通过调用标准输出向串口发送消息的时候,需要重写fputc弱函数,因为在嵌入式C标准库中,标准输出中最后是调用了fputc函数,一个字符一个字符的发送的。
从这个地方,我们就能看的出来,由ISO C标准定义的C语言标准库与嵌入式C标准库的不同了,多在文件系统和操作系统的系统调用的方面有所不同。 在MicroLIB中,fputc函数的默认历程是什么都不做,需要用户重写,HAL库在默认历程中调用用户重写后的fputc函数。
一个小问题?
这里又有一个问题?C语言是如何做到重写和多态的呢?我们知道在C++中,当我们利用面向对象的多态特性的时候,派生类中重写基类中定义的虚函数,使用指向派生类的基类指针调用虚函数,在运行时会发生动态绑定(动态多态)。
C语言中实现多态的方法
下面就介绍几个C语言中实现多态的方法
定义虚函数
MicroLIB 库中的实现方法,定义_weak弱函数。他的原理是:在编译链接的时候,使用用户重写的fputc函数的地址,代替弱函数int fputc(int /ch/, FILE /fp/)函数的地址,如果用户没用重写,就使用弱函数_weak int fputc(int /ch/, FILE /fp/)函数的地址(这时的编译器对每个函数生成符号表目的时候,还会标记强弱属性,链接器最终会选择强属性的函数或者变量的地址)。
函数指针来模拟
使用函数指针来模拟实现多态
当派生类中的第一个成员基类的指针的时候,第一个成员的地址就是基类的地址。当我们使用基类指针指向派生类的时候,只要在调用派生类中的方法的时候,将他强转之后就可以了。
回调函数实现
最后说一个比较流氓的方法:使用回调函数实现多态。
我们回顾一下多态的本质,同一个接口,不同的上下文中执行不同的实现。
回调函数是将函数指针作为参数,传递给另一个实现多态的函数,这个函数在合适的使用通过函数指针调用函数。这样也就变相的实现了多态。
这样的实现方式呢,通常是在操作系统内核中,如Linux操作系统内核,嵌入式系统,如HAL库,库设计中,如thread库封装LWP轻量级进程...
总结
C语言标准库在大体上可以分为两类,一类是由ISO C标准定义的C语言标准库,另一类是嵌入式C标准库,这两类不是完全不同的两个库,而是同一标准的不同实现。
C语言标准库,在PC/服务器等环境中,标准库的实现依赖于操作系统(如Linux的glibc、Windows的MSVCRT)。功能完整,但代码体积较大,依赖操作系统。
嵌入式C标准库是对嵌入式系统资源受限的特性(内存小、无操作系统),对标准库进行精简或优化后的实现。
比如:
MicroLIB(ARM Keil专用):高度精简,代码体积小,直接调用用户实现的底层函数(如fputc)。
newlib(GCC常用):功能较完整,但允许用户重定向底层接口(如_write)。
移除对操作系统的依赖(如文件系统、多线程)。
保留标准接口(如printf),但要求用户自行实现硬件相关的底层函数(如字符发送)。