1.关于结构化编程.
写C代码也要有面向对象的思想,即以数据结构为中心,对一个对象的操作应尽量放在一个文件里,不要分散在很多文件里.
下面以设计一个会话(SESSION)为例来说明.
在session.h中先定义SESSION结构
struct SESSION {
unsigned long SID;
SSH *ssh; /* SESSION --> SSH */
...
};
在session.c中写出所有对SESSION进行操作的函数
SESSION *create() {...}
init(SESSION *cs) {...}
release(SESSION *cs) {...}
在SSH.h中定义
struct SSH {
unsigned long time;
SESSION *ss /* SSH --> SESSION */
...
};
在ssh.c中实现对SSH的操作
SSH *create_ssh(const SESSION* ss) {...};
int release_ssh(SSH *ss) {...};
/* SESSION和SSH这两种对象(结构)互相指向对方,是一种面向对象的常见设计手法,相当于对象的组合(而不是继承),
设计模式中的bridge讲的就是这个意思
*/
2.如何为系统设计权限
在设计一个系统时通常需要将登录者分为多种身份,不同的身份对同一资源具有不同的访问权限,如何设计?
这里共有三个概念:登录者,资源,对资源的访问(不同的资源对访问的定义不一样,如有的资源是读,有的资源是读/写/执行,有的资源是收/发等)
以下为相关数据结构:
struct USER {
int UID;
char name[8];
char privilege; /* 假设可取 0,2,3,...,7 */
...
}
在设计时,让登录者对象拥有一个属性:privilege(权限)
struct RES1 {
char name[8];
unsigned char read_pri; /* 对resource的访问(读)位图,例如read_pri=9 (00001001) 可表示为privilege=0或3的 USER可读这个资源*/
unsigned char write_pri; /* 对resource的访问(写)位图 */
unsigned char exec_pri; /* 对resource的访问(执行)位图 */
...
}
这里把对RES1的访问分成了三种:读/写/执行,每种访问都用了一个char来表示其位图,三种访问权限位图总长24(3*8)
对资源RES1的读函数如下:
read_res1(const USER *a,RES1 *res1)
{
if ( res1->read_pri & (1 << a->privilege ) ) {
进行res1的读操作
}
else
{
权限不够
}
}
在具体实现时也可以将read_pri,write_pri,exec_pri合并放在一个unsigned int里,操作时先进行移位,如
struct RES1 {
char name[8];
unsigned int pri; /* 对resource的访问位图(共32位,实际只需要24位,高8位无用),例如 pri=258 (00000000-000000001-00000010)(读-写-执行) 可表示为privilege=0的USER可写,privilege=1 USER可执行*/
...
}
对资源RES1的读函数如下:
read_res1(const USER *a,RES1 *res1)
{
if ( (res1->pri>>16) & (1 << a->privilege ) ) {
进行res1的读操作
}
else
{
权限不够
}
}
总之如果privilege分为n种,访问类型有m种,那这个权限位图至少有(m*n)位.
3.用C语言模拟try-catch
设有传统C语言函数调用序列如下
A()
{
ret=B();
if (ret) {
handle_err();
return(ret);
}
}
B()
{
ret=C();
if (ret) {
handle_err();
return(ret);
}
}
C()
{
...
if (OK)
return 0;
else {
handle_err();
return ret;
}
}
如果这个函数调用嵌套层数很多A->B->C->D->...,每层都有出错处理,然后一层一层地退出来,
有时这种设计显得很繁,很需要一种类似于goto的语句,从一个函数直接跳出到最外层的函数,在C++中try-catch
可用来达到这个目的,在C语言中,应如何做呢?
以下使用C中的setjmp,longjmp模拟实现了C++中的try-catch,
#define TRY /
exception=setjmp(mark);/
if (!exception)
#define CATCH /
if (exception)
#define THROW(a) /
do {/
longjmp(mark,a);/
}/
while(0)
#define HANDLE_EXCEPTION /
do {/
handle_error(exception));/
} while(0)
int exception; /* 全局变量 */
jmp_buf mark;
void A()
{
TRY
{
...
B();
...
return;
}
CATCH
{
HANDLE_EXCEPTION;
}
}
B()
{
C();
}
C()
{
...
if (error_code)
THROW(error_code); /* 直接jmp至A函数*/
}
以上函数只是一个演示模型,不支持多线程。需要说明的是:不推荐使用setjmp,longjmp,因为现代CPU大都采用流水线结构,
而这两个函数会强制CPU清空指令缓存,因此在运行效率上会受到影响.
关于setjmp,longjmp的用法可参见其它参考资料.