一个变量引发的血案

左天提交了代码, 大致测一下, 看起来来都OK啦. 今天准备放出去呢, 结果一来就各种不正常, 程序直接乱崩, Firmware各种跑死, 等等各种现象层出不穷.
所以今天可以是称为史上最悲剧的一天啦, 还是说原因吧, 希望以后不要再犯.

————————————————————————–

在之前的软件中使用了类似这样一个结构体:

struct a {
int a;
int b;
int c;
int d;
};

这个结构体类似一个全局结构体, 在a.exe与b.dll中同时使用. 但是a.exe与b.dll是由不同的人维护的. 在近期发现要实现某种功能必须要向struct a中加入字段a1, 于是我就将上述代码修改如下:

struct a {
int a;
int a1;
int b;
int c;
int d;
};

然后我编译了a.exe然后将之前的b.dll拷入安装目录下, 测试软件功能时发现都OK, 因为这时候Firmware压根儿就没跑, 今天在具体测试时, 是Firmware各种死啊(b.dll中从struct a中的变量导出另一种结构体, 给Firmware使用). 下班前才发现, 因为我把变量int a1放在了 int a后面, 而b.dll还是以原来的结构体来取数据, 这样他取出来的数据永远偏了4个byte.而Firmware获得信息不正确, 所以才各种死.
原因很简单, 但是死的很难看.

————————————————————————–

所以如果新增加功能时代码中有类似成员变量需要添加, 那么尽可能的将新加的成员变量放在结构体的最后, 保正向低版本代码兼容. 正确的修改如下:

struct a {
int a;
int b;
int c;
int d;
int a1;
};

这样就能兼容旧的代码, 因为旧的代码中有可能某些DLL没有用到这些字段, 那么只要不影响他们的正确取值程序就能证明, 道理很简单, 坑也很明显, 谁踩谁知道…

C, C++, Object C 初步比较

这个周末终于忍不住学了一下OC,到今天为止,OC的语法总算过了一遍,调用方式大致也了解了,下面就来比较一下OC,C,C++的不同。

————————————————————————–

OC的语法如下:

//shape.h
@interface shape

- (void) set_color: (int) cl;
- (void) draw;

@end

//shap.m
@implementation shape
{
int color;
}

- (void) set_color: (int) cl
{
color = cl;
}

- (void) do_stuff
{
color++;
}
- (void) draw
{
[self do_stuff];
printf("draw shape, color:%dn", color);
}
@end

//main.m
int main(int argc, const char *argv[])
{
shape *p;

p = [[shape alloc] init];
[p set_color: 3];
[p draw];

return 0;
}

C语法如下:

//shape.h
struct shape * shape_create(void);
void set_color(struct shape *self, int color);
void draw(struct self);

//shape.c
#include
struct shape {
int shape;
};

struct shape_create()
{
struct shape *p = (struct shape *)malloc(sizeof(struct shape));

return p;
}

void set_color(struct shape *self, int color)
{
self->color = color;
}

static void do_stuff(struct shape *self)
{
self->color++;
}

void draw(struct shape *self)
{
do_stuff(self);
printf("draw shape, color:%dn", self->color);
}

//main.c
int main(int argc, const char *argv[])
{
struct shape *p;

p = shape_create();

set_color(p, 3);
draw(p);

return 0;
}


C++的语法如下:

//shape.h
class shape {
private:
int color;
private:
void do_stuff(void);
public:
void set_color(int color);
void draw(void);
}
//shape.cpp
void shape::do_stuff(void)
{
color++;
}

void shape::set_color(int cl)
{
color = cl;
}

void shape::draw()
{
do_stuff();
printf("draw shape, color:%dn", self->color);
}

//main.cpp
int main(int argc, const char *argv[])
{
class shape *p;

p = new shape;

p->set_color(3);
p->draw();

return 0;
}


————————————————————————–
1. 关于接口与现分离
从代码上可以看出,do_stuff函数与color变量是类或模块自己内部使用,因此从理论上来说不应该导出到头文件中,这个不符合高内聚原则,下面看一下C, C++, Object C分别是如何实现的:
@C 可以看到在C中,完全可以隐藏color变量与do_stuff这个内部函数,
客户程序从头文件中不会知道do_stuff函数与color变量,
就算客户程序员看了shape.c,
他也不可以调用do_stuff与直接访问color成员变量的.
C做到了接口与行为上的高内聚

@C++ 不管你用到什么东西,都必须在头文件声明出来,
哪怕这些东西是prive的(自己用的), 给人一种赤裸裸的感觉.
同样客户程序虽然知道有color成员变量与do_stuff函数
,但是他不可以调用do_stuff与直接变量color;
C++只做到了实现上的高内聚, 头文件会暴露一定细节

@Objelt C 从Object C的代码形式上可以看到,
Object C同样做到了C那样可以从接口与行为上的高内聚,
但是其实这里面有一个缺陷就是,
在上述代码中虽然没有导出接口do_stuff, 但是如果客户程序员知道阅读了源码的话,其实他是可以调用do_stuff这个函数的.
2. 关于函数调用方式

3. 关于命名空间