原创 C语言的面向对象编程(一)

2014-10-9 07:26 3434 22 8 分类: 消费电子

一、前言

对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。C语言常说是面向过程开发的语言,因为其缺少很多对于面向对象特性的支持。但,这并不影响我们采用面向对象的方式,使用C语言编程。
 

二、基本介绍

在C语言中,采用面向对象开发,有两个地方是要明白的:
1、结构体,
2、函数指针变量。
 
结构体:结构体是对于内存的一种组织方式,结构体成员可以是多个不同类型的变量(这是与数组最大的区别,数组中可以包含多个的变量,但只能是同一种类型)。这样使用结构体做到将多个相关的数据集合(封装)到一起。便于记忆与管理。
函数指针变量:函数指针变量,是一个可以存放函数地址的指针变量。因为它是变量,所以可以被重新赋值;因为它是指针变量,所以可以间接寻址;因为它是函数指针变量,可以将函数地址赋值给它,并通过对它的访问,实现调用函数的目的。
 
由于结构体本身不能定义函数,那么没有办法将函数直接集合(封装)到结构体中。但是,函数指针变量给我们提供了一个间接将函数集合(封装)到一起的方式。我们可以通过调用函数指针变量,实现调用函数的目的。
struct struct_test {
     uint8_t type;
     uint16_t counter;
     uint32_t size;
     void (*fun)(void *input);
};
 
struct struct_test test;
测试内存(Ubuntu gcc编译)分布如下:
test size:12
type:0xbff9fb64  /* 这里进行了字节对齐,多加了一个字节 */
counter:0xbff9fb66
size:0xbff9fb68
fun:0xbff9fb6c
 

三、简单实现

面向对象的本质是对数据和方法(函数)的隐藏,避免对细节的了解。而这一点,正是结构体结合函数指针变量可以做到的。
在这里,着重于对于数据和方法(函数)的隐藏来谈论C语言面面向对象的简单实现。暂时不去谈论,关于面向对象的其他特性例如:继承,组合,多态等。
在C的模块化编程中,将一个.c和.h文件看作一个模块,在实现的时候,尽量使用局部静态变量,减少对外的函数接口,同时尽可能少的调用其它模块中的函数,避免受到其他模块的影响(也即,低耦合,高内聚)。C的面向对象编程与模块化编程,都是为了解决降低编程复杂度,减轻同一时间需要记忆的工作量,所以他们可以也应该联合使用。
简单的例子:
有一个.c文件oo_test.c
#include "oo_test.h"
 
static struct oo_test  oo;
/* struct oo_test  oo; */
/*
void oo_test_init(void)
{
     oo.type = 0;
     oo.counter = 0;
     oo.fun = &test;
}
*/
 
struct oo_test *oo_test_init(void)
{
     oo.type = 0;
     oo.counter = 0;
     oo.fun = &test;
     return &oo;
}
 
static void test(void *input)
{
     …;
}
 
有一个.h文件oo_test.h
#ifndef  OO_TEST_H
#define OO_TEST_H
#include "stdint.h"
 
struct oo_test {
     uint8_t type;
     uint16_t counter;
     void (*fun)(void *input);
};
 
struct oo_test *oo_test_init(void);
/* #define OO               oo  */
/* extern struct oo_test  oo; */
#endif
 
其他文件使用:
xx.c
……
struct oo_test *oo = oo_test_init();
oo.fun();
/* oo_test_init(); */
/* OO.fun();  */
……
(注释中,是另一个种实现方式,这两种方式各有所取。根据使用的情况下,适当选择)
 
在上边提到的简单的例子中,首先避免了C编程中全局变量的问题,通过局部静态变量实现信息传递,做到数据的隐藏;
其次减少C编程中对外的函数接口(这一点是模块化编程,与面向对象编程都强调的一点:减少对外的接口,隐藏内部函数)。
 

四、参考

Contiki操作系统;
《Make Embedded System》。

文章评论5条评论)

登录后参与讨论

1989tie_959541171 2014-10-28 21:59

一语中的哈! 能一眼看出来是在协议处理的地方用的很多。 确实,这种写法在Contiki中是用于处理协议栈的。 由于在嵌入式(MCU级)中很少去回收资源,毕竟是预先开辟多少用多少。所以这里边没有考虑析构。 语言是解决问题的利器。它的使用方式,并不受其本身的局限,而是实际情况和我们的思维局限了它。如何在合适的情况下,合理的使用它需要积累与经验。 这里的使用方式,也是其中的一种。

用户1630203 2014-10-14 19:30

讲得很好,学习了。

xucun915_925777961 2014-10-14 18:48

上位机当然可以用C++或其它面向象的语言,但是对于简单的单片机或arm芯片,很多编译器是不支持C++开发的,一般这种面向对象的方式,再写协议处理时用处是比较大的^_^

用户1406868 2014-10-14 10:11

struct oo_test { uint8_t type; uint16_t counter; void (*fun)(void *input);//这个应该是 // void (*fun)(struct oo_test* this, void *input); }; oo_test_init 应该返回malloc的对象。还要增加一个delete函数。 放在结构内的函数指针实际上是对应C++中的virtual函数,且比C++的更灵活,能够在对象生存期更改函数。 普通的成员函数直接用函数就可以了: void oo_test_XXX(struct oo_test* this, int aaa); 不过这么折腾什么不用C++? 不需要异常的话也可以关闭的。

用户1678053 2014-10-14 09:43

看看

用户874192 2013-12-26 16:14

写的很清晰,支持!

whxmyh_221898651 2013-12-19 16:15

了解一下

用户1277994 2013-12-9 09:29

谢谢分享

用户1297079 2013-12-9 08:48

超级电容应该可以在电路上进行突破
相关推荐阅读
catch2000 2015-07-19 11:44
信号线小电阻的作用
在一块新的PCB上,测试系统能否正常运行的时候,发现系统上电后没有正常启动。  系统框图如下:   在上电的时刻,CPU A(GPIO电平2.6V)会向串口发送启动日志数据,CPU A启动后,...
catch2000 2015-07-05 17:04
协议设计中ACK机制的影响
在TCP/IP中,延时ACK和Nagle算法。  TCP为了同时处理成块数据(通常为512字节的用户数据)和交互数据(通常用户数据比较少,例如不大于10个字节),采用了延时ACK和Nagle算法...
catch2000 2015-05-23 15:48
话说物联网操作系统
最近好多家都宣布推出自己的物联网操作系统。   1. Google将要在Google I/O大会发布的Brillo; 2. 三星推出的Artik芯片搭载Mentor Graphics的...
catch2000 2015-03-31 23:52
不要采用异或来交换两个变量
在进行两个变量的时候,经常会看到有些书误人子弟的推荐使用异或的方式: 方式一 {   x = x ^ y;   y = x ^ y;   x = x ^ y; } 而不是...
catch2000 2014-10-09 07:28
为什么要测试先行
在产品的研发过程中,测试一项至关重要。不论是软件还是硬件。   软件的测试先行,在研发过程中,就做到质量的保证,因为在出现Bug的时候,容易定位Bug,而且即使是在客户端出现Bug,也能够...
我要评论
5
22
关闭 站长推荐上一条 /2 下一条