01 #define PARAM_STR0 "height"
02 #define PARAM_STR1 "width"
03 #define PARAM_STR2 "mode"
04 enum{
05     HEIGHT_PARAM,
06     WIDTH_PARAM,
07     MODE_PARAM,
08     MAX_PARAM
09 };
10 static int s_height_param;
11 static int s_width_param;
12 static int s_mode_param;

如果一不小心,把 HEIGHT_PARAM 对应的 PARAM_STR0 写成了
#define PARAM_STR0 "width"
那么在做文本内容比较时,就会出错。这里拓展说下
#define STR "string"
是什么。这是一个字符串的定义。如下代码
void test(void){
    printf("%s\n",STR);
}

void test(void){
    printf("%s\n","string");
}
一样 。此时,"string",7个字符存储空间(不是6个),会作为一个常量的存储空间,存放在链接后的某个区域,而这个空间是编译过程申请的。你不必在意空间问题。其和
const char str[] = "string"; 很像。不一致的地方是,我们对str的使用更灵活。例如
const char str[] = "string",在A这个C文件里声明。而另一个C文件B里有如下代码
extern const char str[]; //这个通常放在和A同名的头文件里,被B这个C文件#include进来。
void test(void){
    printf("%s\n",str);
}
此时,str被看作外部一个常量的存储空间(你不能对这个空间里的内容进行修改),针对B这个C文件在编译时,并不会额外申请空间,而对于前面的例子 ,B这个文件可不管那么多,看见常量,直接分配个空间使用。
回到前面的讨论,这里我们可以尝试使用struct这个C语言的语法声明。
struct的用法如下
struct STRUCT_NAME{
    int i;
    char c;
    void *p;
} struct_space;
这里是个例子,并不代表{ } 内一定要填写什么。同时 STRUCT_NAME表示一个存储组合的名称,而struct_space表示一个存储空间的名称。类似
char c ; char 表示一个存储类型,而c表示该存储类型的一个申请到的空间。而上述存储组合的类型是什么? struct STRUCT_NAME;
因此你还可以如下书写
struct STRUCT_NAME{
...
};
struct STRUCT_NAME struct_space;
由于这里有三个内容,存储组合的名称,存储类型,存储类型对应的空间的名称。因此存储组合的名称和存储类型对应的空间的名称没有必要不同。例如,如下的方法也是对的。不过在我的团队,谁这么写,踢谁出去。
struct S{
    char a;
    int b;
    char c[10];
};
struct S S;
那么我们如何访问到a或b呢。很简单。 如下
S.a = 0;
S.b = S.a + 1;
你可以尝试 printf("S.a is %d ; S.b is %d \n",S.a,S.b); 测试一下。
S.a 和S.b都表示什么含义呢?
S我们知道是一个空间申请后对应的名称。由于这个空间内部的组织结构是在struct S这个类型声明里描述的。因此编译器并没有默认规则能确定。由此需要你明确提示,究竟使用这个里面的哪个具体空间。这和去拜访客户一样,某某局的某某处,甚至还有某某科室。
那么 a ,b究竟存在这个组织空间里什么地方呢?这个和编译有关。通常,首先按顺序存放。其次,保证每个变量的存储边界至少按照寻址最小单元来存储。有些情况下, 甚至按照对硬件或操作系统有利的方式存储。例如如果32位的系统下,我们将存储内容是32位对齐的。那么假设
S的存储空间的首地址为 0x1000,那么 a也即存放在这里,但是b 并不存放在 0x1001这里,虽然a只要一个8位空间byte。因为这样会导致数据b在传递中,要读取两个 32位的空间,并进行移位的操作,所以 b通常直接存放在0x1004里,而0x1001 到0x1003这3个存储空间,无所谓,就浪费了。由此,你设计struct,需要注意以下几个习惯,当然都是鬼话,不存在必要性 :
1、所有存储子内容,宽度越宽的,越放上面。例如 char c[10] ;那么就放在int b和char a前面。
2、对于任意存储子内容,坚持至少使用32位宽。例如 char c[10]就应该定义成 char c[12];
3、除了用于存储空间收集目的,否则,struct的内容不要太多。尽可能的明确每个struct的逻辑,保证内部的子内容之间存在很强的逻辑关联。
关于第3点,展开讨论一下。例如我们我们定义个坐标,(x,y),每个坐标上有,space_mode(空间类型),space_size(空间尺寸)等4个信息。则使用
struct COOR{
    int x;
    int y;
};
struct SPACE{
    int space_mode;
    int space_size;
};
struct SPACE_ALL{
    struct COOR coor;
    struct SPACE space;
};

struct SPACE_ALL{
    int x;
    int y;
    int space_mode;
    int space_size;
};
要好很多。虽然存储 组织上一样。因为,x,y的逻辑关联更紧密。而x 和 space_size并没有什么对等关联。这样可以保证你对上述空间操作的代码设计,逻辑更简单,描述更清晰,代码利用率也提高。
我上面说了一个收集,这个意思是,当我们存在很多存储空间申明时,我们希望这些存储空间能够集中存放。例如 value.c中有
int g_status;
char *g_pmodel ;
char *g_pcontrol_input;
这三个全局存储空间声明。我们可以如下操作,在value.h中增加
struct GLOBAL{
    int status;
    char *pmodel ;
    char *pcontrol_input;
};
extern struct GLOBAL global;
而对应的 extern char *g_pmodel;等等都可以删除。
而在value.c 里可以直接写一个
GLOBAL global;
现有可能你还看不出好处。记得我们对每个全局存储空间的新增时都要做以下几步
1、在value.c下,写出该空间的声明,例如 char *g_pmodel;
2、在value.h下,写出extern char *g_pmodel;
3、必要时,对应其他C文件,需要#include "value.h"
而现在,你可以把1,2两项的工作,缩减为一个工作。
在value.h的 struct GLOBAL内增加 char *pmodel;就可以了。
别小看只是省了这一步,实际工作中,少一步就少了一个可能的出错点。此处的少,和把简单的事情搞复杂不矛盾,为啥?自己想。而需要很注意的是,这里为什么 可以少了一步?是因为,struct的收集方式,针对一类空间申请是有同样的逻辑描述,例如,需要在.h文件里extern声明一下,需要在 value.c申请一下。由此,这个收集方式,当struct里有一个存储单元被正确验证过,其他存储单元,涉及到上述步骤,也就自然可正确完整。
鬼话:人为的书写和设计,难免有错误。一个好的设计方法,包含了降低人为失误的机理或机制。
struct还有另外一种写法,其实你在参考文献1的 6.7.2.1中可以找到详细内容。我们给出下面一个写法
typedef struct {
    int a;
    char b;
}GLOBAL;
意思是 将 struct { int a ; char b ; } 这个类型定义为 GLOBAL。此时GLOBAL直接就是一个具体的存储类型了,无非是自定义的存储结构。包含了 int a ;和char b;两个存储空间。这样的好处如下:
struct S s1;
GLOBAL g;
显然下面的方式更清晰。同时,S本身并不是一个类型名。所以,你可以
struct S S;
而GLOBAL 是一个被编译器认可的类型名(由typedef导致)。此时
GLOBAL GLOBAL;就是非法的了。
鬼话:约束,有时对你是种帮助。上述struct S S;的书写方式很容易搞混代码文本的逻辑理解。
这里联合前面说的指针讨论一下。我们需要一个存储空间,里面存在的一个值,这个值指向一个地址,对应的空间是一个自定义的存储结构。那么我们可以
GLOBAL *pG;
 (*pG).a = 0;
 (*pG).b = 1;
鬼话: 为什么加(),因为我记不得谁的优先级更高。*还是.。这不是丢脸的事情,甚至我可以强制保持逻辑描述的争取性。比查资料,或者背考试答卷有效的多。相信 我,学校C语言考试如果出现优先级的考题,无非是因为出题者缺乏工程开发经验导致没有更有价值的题目而凑数字的出。实际工程中,即便再有经验的工程师,对 于优先级,仍然会保持()的心态,进行确认,因为有些古怪的符合标准的书写,对于不同的编译器可能有不同的实际执行。没有必要纠结这些形而上学的东西,安 心加(),不会累死机器的。
C语言,提供了个更好的书写方式,完成上述(*pG).a等的操作。如下:
pG->a = 0;
pG->b = pG->a + 1;
这样写,表示无论是对空间的读利用,还是空间的写利用,那么这样操作都可以。实际pG->a完成了什么?我们做个测试。以下给出control.c的清单,注意新添部分
001 #include <stdio.h>
002 #include <stdlib.h>
003 #include <string.h>
004 #include "define_attr.h"
005 #include "control.h"
006 #include "value.h"
007  
008 typedef struct{
009     int height;
010     int width;
011     int mode;
012 }PARAM_S, *P_PARAM_S;
013 static PARAM_S s_param;
014 static P_PARAM_S s_pparam_test = &s_param;
015 #define GET_FILE_SIZE(size,fp) do {long int pos = ftell(fp); fseek(fp,0L,SEEK_END);size = ftell(fp);fseek(fp,pos,SEEK_SET);}while(0)
016 char filename[1024];
017 static long int buf_num = 0;
018 #define TOKEN_MAX_NUM 10
019 static char *s_ptoken_pos[TOKEN_MAX_NUM];
020  
021 #define dNEXT_LINE 0xa
022 #define dRETURN 0xd   
023 #define dSPACE_KEY 0x20
024 #define dTAB_KEY 0x9
025 #define CHECK_ALPHA(c) (((c) != dSPACE_KEY) && ((c) != dTAB_KEY))
026 #define CHECK_LINES(p,i,lines) do {lines += (p[i] == dNEXT_LINE);}while (0)
027  
028 #define PARAM_MAX_NUM 3
029 static int split_token(char *pline,char *ppos[]);
030 static int check_grammar(int argc,char *argv[]);
031 static int set_param(int mode,int value);
032 static int print_param(void);
033 static int split_line(char *pbuf){
034     int i;   
035     int lines = 0;
036     char *pline = pbuf;
037     __PRINT_FUNC();
038     i = 0;
039          
040     while (i < buf_num){
041  
042         CHECK_LINES(pbuf,i,lines);
043         if (pbuf[i] == dNEXT_LINE){
044             int tokens;
045             pbuf[i] = 0;
046             printf("%s \n",pline);
047             tokens = split_token(pline,s_ptoken_pos);
048             printf("%d\n",tokens);
049             //printf("check return %d\n",
050             check_grammar(tokens,s_ptoken_pos);
051             pline = pbuf + i+1;
052              
053              
054         }else  if (pbuf[i]  == dRETURN){
055             pbuf[i] = 0;
056             pline = pbuf+i+1;
057         }
058      
059         i++;
060     }
061        print_param();
062     printf("the lines is %d\n",lines);
063  
064     return 0;
065 }   
066 static int split_token(char *pline,char *ppos[]){
067     int tokens = 0;
068  
069     __PRINT_FUNC();
070     while ((*pline)&&(tokens < TOKEN_MAX_NUM)){
071         if (CHECK_ALPHA(pline[0])){
072             ppos[tokens++] = pline;
073                         pline++;
074             while (CHECK_ALPHA(pline[0])){
075                 if (pline[0] == 0){
076                     goto LABEL_return_split_token;
077                 }
078                 pline++;
079             }           
080         }
081         pline[0] = 0;
082         pline++;
083     }
084      
085 LABEL_return_split_token:
086     printf("the tokens is %d !\n",tokens);   
087     return tokens;
088 }
089 #define PARAM_STR0 "height"
090 #define PARAM_STR1 "width"
091 #define PARAM_STR2 "mode"
092 enum{
093     HEIGHT_PARAM,
094     WIDTH_PARAM,
095     MODE_PARAM,
096     MAX_PARAM
097 };
098 static int print_param(void){
099     int a =0;
100     printf("only test %d\n",a);
101     printf("%s -> %d\n",PARAM_STR0,s_param.height);
102     printf("%s -> %d\n",PARAM_STR1,s_param.width);
103     printf("%s -> %d\n",PARAM_STR2,s_pparam_test->mode);
104     return 0;
105 }
106 static int check_grammar(int argc,char *argv[]){
107     int value;
108     int mode = -1;
109     __PRINT_FUNC();
110     if (argc != 3){
111         return 1;
112     }
113      
114     if ((strcmp(argv[0],PARAM_STR0) == 0)){
115         mode = 0;
116     }else if ((strcmp(argv[0],PARAM_STR1) == 0)){
117         mode = 1;
118     }else if ((strcmp(argv[0], PARAM_STR2) == 0)){
119         mode = 2;
120     }else {
121         return 2;
122     }
123     if (strcmp(argv[1],"=") != 0){
124         return 3;
125     }
126     value = atoi(argv[2]);
127     set_param(mode,value);
128     return 0;
129 }
130 static int set_param(int mode,int value){
131     __PRINT_FUNC();
132     switch (mode){
133         case HEIGHT_PARAM: s_param.height = value;
134          //printf("%s -> %d\n",PARAM_STR0,value);
135         break;
136         case WIDTH_PARAM: s_param.width = value;
137         // printf("%s -> %d\n",PARAM_STR1,value);
138          break;
139         case MODE_PARAM:s_param.mode = value;
140         //printf("%s -> %d\n",PARAM_STR2,value);
141          break;
142         default:
143             printf("mode is error!\n");
144     }
145     return 0;
146 }
147 static void read_param_default(void){
148     __PRINT_FUNC();
149     return;
150 }
151 static int read_param_from_file(char * filename){
152     FILE *fp;
153     long int file_size;
154     long int read_size;
155     __PRINT_FUNC();
156     fp = fopen(filename,"rt");
157     if (fp == 0) return 1;
158     GET_FILE_SIZE(file_size,fp);
159     if (file_size >= CONTROL_INPUT_SIZE){
160         fclose(fp);
161         return 2;
162     }
163  
164     buf_num = read_size = fread(g_pcontrol_input,sizeof(char),file_size,fp);
165     g_pcontrol_input[buf_num] = 0;
166     printf("file size is %ld,read is %ld !\n",file_size,read_size);
167     fclose(fp);
168      
169     return split_line(g_pcontrol_input);
170 }
171  
172 void control(int flag){
173     if (flag){
174         read_param_from_file(filename);
175     }else{
176         read_param_default();
177     }
178     return;
179 }

    由于前面测试的正确,所以我们果断将set_param里的测试注释掉。并在split_line后,统一打印出每个参数的信息。而此时各个参数已经被 收集到s_param里。注意typedef struct { ...} PARAM_S ,*P_PARAM_S;这里实际上申明了两个类型,后一个是指针类型,其对应空间里存放的值,指向上述符合结构体逻辑关系的存储空间。
    我们反汇编看看 s_param.height 和s_param_test->mode都有什么不同。对应的链接后的反汇编,如下
00008850 <print_param>:
    8850:    b580          push    {r7, lr}
    8852:    af00          add    r7, sp, #0
    8854:    f249 03f0     movw    r3, #37104    ; 0x90f0
    8858:    f2c0 0300     movt    r3, #0
    885c:    f241 4220     movw    r2, #5152    ; 0x1420
    8860:    f2c0 0201     movt    r2, #1
    8864:    6812          ldr    r2, [r2, #0]
    8866:    4618          mov    r0, r3
    8868:    4611          mov    r1, r2
    886a:    f7ff ee66     blx    8538 <_init+0x2c>
    886e:    f249 1300     movw    r3, #37120    ; 0x9100
    8872:    f2c0 0300     movt    r3, #0
    8876:    f241 4214     movw    r2, #5140    ; 0x1414
    887a:    f2c0 0201     movt    r2, #1
    887e:    6812          ldr    r2, [r2, #0]
    8880:    4618          mov    r0, r3
    8882:    f249 110c     movw    r1, #37132    ; 0x910c
    8886:    f2c0 0100     movt    r1, #0
    888a:    f7ff ee56     blx    8538 <_init+0x2c>
    888e:    f249 1300     movw    r3, #37120    ; 0x9100
    8892:    f2c0 0300     movt    r3, #0
    8896:    f241 4214     movw    r2, #5140    ; 0x1414
    889a:    f2c0 0201     movt    r2, #1
    889e:    6852          ldr    r2, [r2, #4]
    88a0:    4618          mov    r0, r3
    88a2:    f249 1114     movw    r1, #37140    ; 0x9114
    88a6:    f2c0 0100     movt    r1, #0
    88aa:    f7ff ee46     blx    8538 <_init+0x2c>
    88ae:    f249 1300     movw    r3, #37120    ; 0x9100
    88b2:    f2c0 0300     movt    r3, #0
    88b6:    f241 32fc     movw    r2, #5116    ; 0x13fc
    88ba:    f2c0 0201     movt    r2, #1
    88be:    6812          ldr    r2, [r2, #0]
    88c0:    6892          ldr    r2, [r2, #8]
    88c2:    4618          mov    r0, r3
    88c4:    f249 111c     movw    r1, #37148    ; 0x911c
    88c8:    f2c0 0100     movt    r1, #0
    88cc:    f7ff ee34     blx    8538 <_init+0x2c>
    88d0:    f04f 0300     mov.w    r3, #0
    88d4:    4618          mov    r0, r3
    88d6:    bd80          pop    {r7, pc}
    注意一下:
    8876:    f241 4214     movw    r2, #5140    ; 0x1414
    887a:    f2c0 0201     movt    r2, #1
    887e:    6812          ldr    r2, [r2, #0]
    ==================
    8896:    f241 4214     movw    r2, #5140    ; 0x1414
    889a:    f2c0 0201     movt    r2, #1
    889e:    6852          ldr    r2, [r2, #4]
    ==================
    88b6:    f241 32fc     movw    r2, #5116    ; 0x13fc
    88ba:    f2c0 0201     movt    r2, #1
    88be:    6812          ldr    r2, [r2, #0]
    88c0:    6892          ldr    r2, [r2, #8]
    ==================
    这三块,我猜也能猜出来,就是处理 s_param.height,s_param.width ,s_param_test->mode的工作。你问我,你怎么猜不出来,其实这没什么学问,只是个经验的问题。或许你工作个几个月,就也能猜出来了。
    (注:以上具体的地址,可能根据你的机器环境有所不同。重点的是哪些指令和寄存器如 movw r2,#xxxx, ldr r2,[r2,#0]等)
    这里不强调谁能猜出来,我们看区别。
    前面两次,对r2 设置都是相同,表示都是一个存储空间里的地址。而
    ldr r2,[r0,#XX],这个XX不同。一个0,一个是4。为什么?我们回顾下PARAM_S的定义,
    typedef struct{
        int height;
        int width;
        int mode;
    }PARAM_S;
    PARAM_S s_param;
    自然,height存放在这个空间的最起始的地方,由于是32位,所以width对应的是偏移4的位置。
    那么第三块,后面一个语句和前面和很像,而且我们知道是取s_param这个空间里偏移8的位置的,也即mode的内容。但你会发现多了一次 ldr。
    ldr r2,[r2,#0] 就是 s_pparam_test的操作,将其存储空间里的值取出来,存放到r2中。这本身就是s_param的地址。由于s_param的实际地址是链接器设 置,因此,对于s_param的操作,就不需要ldr了,直接用 movw这样指令,对r2直接设置值就可以了。
    ldr r2,[r2,#8],这和前面通过 ldr r2,[r2,#4]取出s_param里width一样,无非是取出mode。
    而我特地加上了一个测试代码,对buf_num进行打印操作。你可以注意一下
    885c:    f241 4220     movw    r2, #5152    ; 0x1420
    8860:    f2c0 0201     movt    r2, #1
    8864:    6812          ldr    r2, [r2, #0]
        这表示什么含义呢?一个存储空间的访问,和一个自定义的存储结构体空间里的单元访问并没有什么指令操作上的区别。如
            8864:    6812          ldr    r2, [r2, #0] //buf_num
           887e:    6812          ldr    r2, [r2, #0]//s_param.height
              887e:    6812          ldr    r2, [r2, #4]//s_param.widht
              所以千万别人为s_param.height多了个.操作,就会导致计算逻辑变复杂了。这是因为编译器可以实现算出结构体内各个单元在这个整体存储空间内的值。
              那么,我们自己怎么显示的获取这些值呢?以下,给出一个C的#define ,
1 #define __BIAS_STRUCT(type,member) (unsigned long)&(((type *)0)->member)

    那么我们将print_param修改如下
1 #define __BIAS_STRUCT(type,member) (unsigned long)&(((type *)0)->member)
2 static int print_param(void){
3  
4     printf("%s(%ld) -> %d\n",PARAM_STR0,__BIAS_STRUCT(PARAM_S,height),s_param.height);
5     printf("%s(%ld)-> %d\n",PARAM_STR1,__BIAS_STRUCT(PARAM_S,width),s_param.width);
6     printf("%s(%ld) -> %d\n",PARAM_STR2,__BIAS_STRUCT(PARAM_S,mode),s_pparam_test->mode);
7     return 0;
8 }

    你可以编译链接,运行看一下,是否还有如下内容:
height(0) -> 3
width(4)-> 5
mode(8) -> 1
    我们分析一下上面的一定。如果给入PARAM_S ,和height这两个内容,则    
    __BIAS_STRUCT(PARAM_S,height) 被替换为
    (unsigned long)&(((PARAM_S *)0)->height)
    从()的最里面看。 (PARAM_S *) 0,这是将0强制转换为(PARAM_S*),((PARAM_S *)0)->height 是取一个PARAM_S的类型结构的存储空间里height的单元。&(((PARAM_S *)0)->height) 是将这个单元的对应地址取出来。(unsigned long)是将这个地址强制转换为正整数。由于整个存储空间,是以0地址为起始,所以&(((PARAM_S *)0)->height)取出来的数减去0,自然是这个height在整个结构体内的偏移位置。我们可以反汇编看下,机器都怎么操作的。
    你可以注意到有以下代码,你忽略你的机器上的绝对地址。
    8864:    6814          ldr    r4, [r2, #0]
    8866:    4618          mov    r0, r3
    8868:    f249 01f8     movw    r1, #37112    ; 0x90f8
    886c:    f2c0 0100     movt    r1, #0
    8870:    f04f 0200     mov.w    r2, #0
    8874:    4623          mov    r3, r4
    8876:    f7ff ee60     blx    8538 <_init+0x2c>    
    由于我们printf("%s(%ld) -> %d\n",PARAM_STR0,__BIAS_STRUCT(PARAM_S,height),s_param.height);的参数改动。所以
    __BIAS_STRUCT(PARAM_S,height)最终的值存放在r2,原先r2中存放的s_param.height被挪到r3中。
    此时,    8870:    f04f 0200     mov.w    r2, #0 ,表示编译器对上述复杂的定义     (unsigned long)&(((PARAM_S *)0)->height)
    直接计算出了结果。
鬼话:这样做有啥好处?你去看看linux内核中涉及list的相关源代码,就知道了。别怀疑,那些世界顶级的C工程师,写出的代码,实际你现在也能写 出。无非你的经验不足,尚不能灵活应用。这里送很多新手一句张狂的话,“当你抬头景仰所谓高手,牛人时,你已经输,再牛也是人,没必要怀疑他们使用了某种 你没有权利使用的高端设计方法”
    这里需要再重复的说明一下。
#define __BIAS_STRUCT(type,member) (unsigned long)&(((type )0).member)
    是不对的。我们从另一个角度来解释。 (type)a表示,a是个存储空间。我们对a里面的值,或某个具体的值,逻辑上(必要时,机器指令有新增动作保证逻辑正确)进行转换。例如你可以看作有符号,或无符号。这对后期计算的逻辑有作用。例如
    signed char sc;
    unsigned char uc;
    sc = (signed char)127;
    uc = (unsigned char)sc;
    sc++;
    uc++;
    此时,sc里等于0,而uc里等于128.不信你打印试试。
    uc = (unsigned char)sc;表示取出sc里的值,强制转换为无符号char类型,存储到uc里。
    sc= (signed char)127;表示,对127这个值,强制转换为有符合char类型,存储到sc里。
    而(type ) 0,表示什么意思?将0,按照type的类型来理解。如果type是 PARAM_S,如下
    s = (PARAM_S)0;充其量是对s进行全部设置0的操作。此时就是有作用,也只是保存到一个PARAM_S的存储空间里。而我们要的是门牌号码,不是家 里的东西。因此 0应该我们设想的是对某个PARAM_S的存储空间的地址的描述。因此自然是(PARAM_S *)0,即,将0作为一个地址,这个地址,指向一个PARAM_S类型的存储空间。那么
    &((PARAM_S *)0->height)自然是对存储在0这个地址,我们看做PARAM_S类型的存储空间里,height这个单元的取地址操作。
    这有还有什么用?我们回顾一下开头说的易出错的问题。我们如何保证"height"和 s_param.height 对应呢?以下给出一个打击学院派,坚决不用switch的设计方法。好处后面提。先把control.c的完整代码列出,注意新增部分和注释掉的部分。
001 #include <stdio.h>
002 #include <stdlib.h>
003 #include <string.h>
004 #include "define_attr.h"
005 #include "control.h"
006 #include "value.h"
007  
008 typedef struct{
009     int height;
010     int width;
011     int mode;
012     float testf;
013 }PARAM_S, *P_PARAM_S;
014 static PARAM_S s_param;
015 static P_PARAM_S s_pparam_test = &s_param;
016 #define PARAM_MAX_NUM 4
017 #define __BIAS_STRUCT(type,member) (unsigned long)&(((type *)0)->member)
018 #define __BIAS_PARAM(member) __BIAS_STRUCT(PARAM_S,member)
019 #define __SET_BIAS(type,ps,bias) *(type *)((void *)(ps) + bias)
020 #define PARAM_STR_MAX_SIZE 20
021  
022 enum{
023     _INT_MODE,
024     _FLOAT_MODE,
025     _MAX_MODE
026 };
027 typedef struct{
028     char *str;
029     unsigned long bias;
030     char* default_value;
031     int type;
032 }PARAM_TAB_S;
033 typedef PARAM_TAB_S * P_PARAM_TAB_S;
034  
035  
036 const static PARAM_TAB_S c_param_tab[PARAM_MAX_NUM] = {
037 {"width",__BIAS_PARAM(width),"0",_INT_MODE},
038 {"height",__BIAS_PARAM(height),"0",_INT_MODE},
039 {"mode",__BIAS_PARAM(mode),"0",_INT_MODE},
040 {"ftest",__BIAS_PARAM(testf),"1.0f",_FLOAT_MODE}
041 };
042 typedef  void (*_SET_FUNC)(unsigned long ,char *) ;
043 static void set_int_param(unsigned long bias,char *s){
044     __SET_BIAS(int,s_pparam_test,bias) = atoi(s);
045 }
046 static void set_float_param(unsigned long bias,char *s){
047     __SET_BIAS(float,s_pparam_test,bias) = (float)atof(s);
048 }
049 _SET_FUNC set_param_func[_MAX_MODE] = {set_int_param,set_float_param};
050  
051  
052 #define GET_FILE_SIZE(size,fp) do {long int pos = ftell(fp); fseek(fp,0L,SEEK_END);size = ftell(fp);fseek(fp,pos,SEEK_SET);}while(0)
053 char filename[1024];
054 static long int buf_num = 0;
055 #define TOKEN_MAX_NUM 10
056 static char *s_ptoken_pos[TOKEN_MAX_NUM];
057  
058 #define dNEXT_LINE 0xa
059 #define dRETURN 0xd   
060 #define dSPACE_KEY 0x20
061 #define dTAB_KEY 0x9
062 #define CHECK_ALPHA(c) (((c) != dSPACE_KEY) && ((c) != dTAB_KEY))
063 #define CHECK_LINES(p,i,lines) do {lines += (p[i] == dNEXT_LINE);}while (0)
064  
065  
066 static int split_token(char *pline,char *ppos[]);
067 static int check_grammar(int argc,char *argv[]);
068 //static int set_param(int mode,int value);
069 static int print_param(void);
070 static int split_line(char *pbuf){
071     int i;   
072     int lines = 0;
073     char *pline = pbuf;
074     __PRINT_FUNC();
075     i = 0;
076          
077     while (i < buf_num){
078  
079         CHECK_LINES(pbuf,i,lines);
080         if (pbuf[i] == dNEXT_LINE){
081             int tokens;
082             pbuf[i] = 0;
083             printf("%s \n",pline);
084             tokens = split_token(pline,s_ptoken_pos);
085             printf("%d\n",tokens);
086             //printf("check return %d\n",
087             check_grammar(tokens,s_ptoken_pos);
088             pline = pbuf + i+1;
089              
090              
091         }else  if (pbuf[i]  == dRETURN){
092             pbuf[i] = 0;
093             pline = pbuf+i+1;
094         }
095      
096         i++;
097     }
098        print_param();
099     printf("the lines is %d\n",lines);
100  
101     return 0;
102 }   
103 static int split_token(char *pline,char *ppos[]){
104     int tokens = 0;
105  
106     __PRINT_FUNC();
107     while ((*pline)&&(tokens < TOKEN_MAX_NUM)){
108         if (CHECK_ALPHA(pline[0])){
109             ppos[tokens++] = pline;
110                         pline++;
111             while (CHECK_ALPHA(pline[0])){
112                 if (pline[0] == 0){
113                     goto LABEL_return_split_token;
114                 }
115                 pline++;
116             }           
117         }
118         pline[0] = 0;
119         pline++;
120     }
121      
122 LABEL_return_split_token:
123     printf("the tokens is %d !\n",tokens);   
124     return tokens;
125 }
126 #if 0
127 #define PARAM_STR0 "height"
128 #define PARAM_STR1 "width"
129 #define PARAM_STR2 "mode"
130 enum{
131     HEIGHT_PARAM,
132     WIDTH_PARAM,
133     MODE_PARAM,
134     MAX_PARAM
135 };
136 //#define __BIAS_STRUCT(type,member) (unsigned long)&(((type *)0)->member)
137 #endif
138 static int print_param(void){
139  
140     printf("s_param.height -> %d\n",s_param.height);
141     printf("s_param.width -> %d\n",s_param.width);
142     printf("s_param.mode -> %d\n",s_param.mode);
143     printf("s_param.testf -> %f\n",s_param.testf);   
144     return 0;
145 }
146 static int check_grammar(int argc,char *argv[]){
147  
148     int mode = -1;
149     __PRINT_FUNC();
150     if (argc != 3){
151         return 1;
152     }
153     for (mode =0; mode < PARAM_MAX_NUM ; mode++){
154         if (strcmp(argv[0],c_param_tab[mode].str) ==0) break;
155     }
156     if (mode >= PARAM_MAX_NUM){
157         return 2;
158     }
159  
160     if (strcmp(argv[1],"=") != 0){
161         return 3;
162     }
163      
164     set_param_func[c_param_tab[mode].type](c_param_tab[mode].bias,argv[2]);
165  
166     return 0;
167 }
168 #if 0
169 static int set_param(int mode,int value){
170     __PRINT_FUNC();
171     switch (mode){
172         case HEIGHT_PARAM: s_param.height = value;
173          //printf("%s -> %d\n",PARAM_STR0,value);
174         break;
175         case WIDTH_PARAM: s_param.width = value;
176         // printf("%s -> %d\n",PARAM_STR1,value);
177          break;
178         case MODE_PARAM:s_param.mode = value;
179         //printf("%s -> %d\n",PARAM_STR2,value);
180          break;
181         default:
182             printf("mode is error!\n");
183     }
184     return 0;
185 }
186 #endif
187 static void read_param_default(void){
188     int mode;
189     __PRINT_FUNC();
190     for (mode =0;mode < PARAM_MAX_NUM ; mode++){
191         set_param_func[c_param_tab[mode].type](c_param_tab[mode].bias,c_param_tab[mode].default_value);
192     }
193     return;
194 }
195 static int read_param_from_file(char * filename){
196     FILE *fp;
197     long int file_size;
198     long int read_size;
199     __PRINT_FUNC();
200     read_param_default();
201     fp = fopen(filename,"rt");
202     if (fp == 0) return 1;
203     GET_FILE_SIZE(file_size,fp);
204     if (file_size >= CONTROL_INPUT_SIZE){
205         fclose(fp);
206         return 2;
207     }
208  
209     buf_num = read_size = fread(g_pcontrol_input,sizeof(char),file_size,fp);
210     g_pcontrol_input[buf_num] = 0;
211     printf("file size is %ld,read is %ld !\n",file_size,read_size);
212     fclose(fp);
213      
214     return split_line(g_pcontrol_input);
215 }
216  
217 void control(int flag){
218     if (flag){
219         read_param_from_file(filename);
220     }else{
221         read_param_default();
222     }
223     return;
224 }

以上我分段进行解释。
1 typedef struct{
2     int height;
3     int width;
4     int mode;
5     float testf;
6 }PARAM_S, *P_PARAM_S;
 
   这里我们增加了一个float 类型的存储单元。无非是想证明这种方法对float的类型的参数也可以正确识别。
   #define __SET_BIAS(type,ps,bias) *(type *)((void *)(ps) + bias) 是用于对一个结构体存储空间里的某个单元进行访问,而通过该单元在该整体的结构体内的偏移量位置决定。例如 width在PARAM_S的偏移量是4则
   __SET_BIAS(int,s_pparam_test,width) 为
   *(int*)((void*)s_pparam_test + 4)
 
1 enum{
2     _INT_MODE,
3     _FLOAT_MODE,
4     _MAX_MODE
5 };

这里枚举了两种类型,_MAX_MODE写出来的好处在于
_SET_FUNC set_param_func[_MAX_MODE] ;
对应的typedef  void (*_SET_FUNC)(unsigned long ,char *) ;就不说了。前面讲过函数指针类型的定义。
这个函数指针数组的大小可以非常简单对应,且不会书写出错。
1 typedef struct{
2     char *str;
3     unsigned long bias;
4     char *default_value;
5     int type;
6 }PARAM_TAB_S;

这是个针对参数分析的结构体。包含了,参数在文本文件中的名词,当前参数在PARAM_S的结构体内的偏移量,default_value你可以从 read_param_default函数被调用的位置发现用途。为什么我们的config_attr 没有testf,s_param.testf还被设置了1。
注意
1 for (mode =0; mode < PARAM_MAX_NUM ; mode++){
2         if (strcmp(argv[0],c_param_tab[mode].str) ==0) break;
3     }
4      
5     for (mode =0;mode < PARAM_MAX_NUM ; mode++){
6         set_param_func[c_param_tab[mode].type](c_param_tab[mode].bias,c_param_tab[mode].default_value);
7     }

    这些代码的设计。
鬼话:以上代码,算我自己的发明创造。我不介意,学院派又开始说,for循环里调用函数指针是多么的没效率。我指向说,在代码设计中,不出错,逻辑清晰, 新增逻辑对应的新增代码量少,是开发效率。至于运行效率,那是整个设计逻辑正确,模块逻辑稳定后,优化的事情,优化和效率是两会事。如果诸位迷信学院派的 说法,不放我们比试一下,下面的新增需求。
假设我们PARAM_S,因为设计的新增,需要增加一个参数 vec(速度) ,类型是double。那么我需要改动的地方,有几个
    首先,参数的增加,修改
1 #define PARAM_MAX_NUM 5

    和
1 typedef struct{
2     double vec;
3     int height;
4     int width;
5     int mode;
6     float testf;
7     }PARAM_S, *P_PARAM_S;

    如果你跟随学院派,不知到把vec任意放某个位置,是否会导致额外的已有正确代码挪动。
    其次,我们需要增加参数表的内容
1 const static PARAM_TAB_S c_param_tab[PARAM_MAX_NUM] = {
2  
3 {"width",__BIAS_PARAM(width),"0",_INT_MODE},
4 {"height",__BIAS_PARAM(height),"0",_INT_MODE},
5 {"mode",__BIAS_PARAM(mode),"0",_INT_MODE},
6 {"ftest",__BIAS_PARAM(testf),"1.0f",_FLOAT_MODE},
7 {"vec",__BIAS_PARAM(vec),"5.02",_DOUBLE_MODE}
8 };

    注意,这里并需要 将 vec的位置和PARAM_S里申明位置相同,此处放在了最后。
    由于是double的处理。因此多了
1 enum{
2     _INT_MODE,
3     _FLOAT_MODE,
4     _DOUBLE_MODE,
5     _MAX_MODE
6 };

    并新增了函数
1 static void set_double_param(unsigned long bias ,char *s){
2     __SET_BIAS(double,s_pparam_test,bias) = atof(s);
3 }

    同时扩建了函数指针的内容。
1 _SET_FUNC set_param_func[_MAX_MODE] = {set_int_param,set_float_param,set_double_param};

    对应,很傻乎乎的学了学院派的一个书写方法,在print_param函数中增加了内容。
1 static int print_param(void){
2      
3     printf("s_param.height -> %d\n",s_param.height);
4     printf("s_param.width -> %d\n",s_param.width);
5     printf("s_param.mode -> %d\n",s_param.mode);
6     printf("s_param.testf -> %f\n",s_param.testf);
7     printf("s_param.vec -> %f\n",s_param.vec);       
8     return 0;
9 }

其实正确的方式应当如下:
1 static int print_param(void){
2     int mode;
3     for (mode =0; mode < PARAM_MAX_NUM; i++){
4         print_param_type[c_param_tab[mode].type](mode);
5     }
6 }

使用函数指针进行调用打印。由于是打印,我就不搞那么复杂了。但是后果就是你没新增一个参数就得多写一次printf的内容。
鬼话:为什么新手写代码,一定逻辑复杂,或内容较多,就总是错来错去。是因为一开始,真把简单的事情搞简单了。当我们把简单的事情搞复杂,如 原先 直接 if 一个个用常量的比较方法,并在 set_param 中用switch一个个比较设置,这个看似简单,却不能让已有代码尽可能的被后续的新增逻辑复用。相信我,努力的琢磨如何让具备类似逻辑描述的代码,真正 的实现同类描述,例如
set_param_func[c_param_tab[mode].type](c_param_tab[mode].bias,c_param_tab[mode].default_value);
或 print_param_type[c_param_tab[mode].type](mode);(此对应代码未写出)
将有助于你提升代码设计的效率。
以下进一步,给出更极端的例子,不过属于少儿不宜,请勿模仿。
我们看一下
1 static void set_double_param(unsigned long bias ,char *s){
2     __SET_BIAS(double,s_pparam_test,bias) = atof(s);
3 }
4 static void set_int_param(unsigned long bias,char *s){
5     __SET_BIAS(int,s_pparam_test,bias) = atoi(s);
6 }
7 static void set_float_param(unsigned long bias,char *s){
8     __SET_BIAS(float,s_pparam_test,bias) = (float)atof(s);
9 }

他们有很多相似处。你将不相似的地方提出来如下
1 static void set_X_param(unsigned long bias,char *s){
2     __SET_BIAS(X,s_pparam_test,bias) = XX(s);
3 }

那么我们可以如下定义
1 #define __FUNC_SET_PARAM(X,XX,XXX) static void set_##X##_param(unsigned long bias ,char *s){\
2 __SET_BIAS(X,s_pparam_test,bias) = XX(s);\
3 }

则上述的代码可以用以下的替换
01 #if 1
02 #define __FUNC_SET_PARAM(X,XX) static void set_##X##_param(unsigned long bias ,char *s){\
03 __SET_BIAS(X,s_pparam_test,bias) = XX(s);\
04 }
05 __FUNC_SET_PARAM(double,atof)
06 __FUNC_SET_PARAM(int,atoi)
07 __FUNC_SET_PARAM(float,(float)atof)
08 #else
09 static void set_double_param(unsigned long bias ,char *s){
10     __SET_BIAS(double,s_pparam_test,bias) = atof(s);
11 }
12 static void set_int_param(unsigned long bias,char *s){
13     __SET_BIAS(int,s_pparam_test,bias) = atoi(s);
14 }
15 static void set_float_param(unsigned long bias,char *s){
16     __SET_BIAS(float,s_pparam_test,bias) = (float)atof(s);
17 }
18 #endif

set_##X##_param,如果XX为 int ,则会被替换为,set_int_param
为什么我说这是个少儿不宜的事情,其实即便你已经成年,也不该如此书写代码。道理很简单。如果是
set_string_param的函数,内部不能
__SET_BIAS(X,s_pparam_test,bias) = XX(s);简单替换怎么办?
鬼话:别嫉妒那些更高级的语言,有什么重载,多态的名词,其实无非如上述一样的设计。任何语言,如果在一个函数里,存在一条语句,可以针对不同类型进行类 似的操作,如同 A = B+C,而不在乎A,B,C的类型。他实际编译后,和你上述写的各种类型的函数实现,并使用对应函数指针进行链接没有区别。无非,C语言,需要比较土的写 出来。
我们需要实现上述设计,脱离不了struct这个万能的打包器。为什么?我们看下如下定义
01 typedef struct{
02     int zero : 1;
03     unsigned int overflow : 1;
04     unsigned int data : 5;
05     unsigned int extend : 1;
06 } FLAG_S;
07  FLAG_S ff;
08  ff.zero = 3;
09  ff.overflow = 1;
10  ff.data = 27;
11  ff.extend = 3;

 你尝试将这些值打印出来看看。
1 printf("%x : %d : %d : %d : %d\n",*(char*)&ff,ff.zero,ff.overflow,ff.data,ff.extend);

 这里表示 ,ff所对应的存储空间,第0为,给zero使用,所以即便你赋值为3,仍然设置个1,由于是int型,带符号,所以输出为-1,而
 ff.extend虽然也只有一位,也是只能设置1,虽然赋值为3,但打印确实1
 16进制打印出的 ffffffef,实际真正有效的ef对应二进制为
  1 1 1 0 1 1 1 1
  实际与上述ff内的单元对应如下
  1 | 1 1 0 1 1| 1 | 1
  extend | data | overflow | zero
同时你应该好好读读,参考文献1 的6.7.8.6的例子的解释。其实参考文献 1中 设计struct的内容,都应该好好读读。

     如果你仍然坚持使用书本的C语言设计方法,不妨我们继续增加逻辑。对各种参数我们增加范围限制,如果当超出范围,自动将参数设置成默认值。看看那种方法能更快,更有效,更保证正确性的设计出来。

鬼话:模块化编程在局部细节上的一个思想就是尽可能的抽象出雷同逻辑。使用数组,结构体,特别是函数指针,宏定义,等方式来实现。抽象抽象再抽象,即是学 好数学的学问,也是编好程序的学问。努力的把直白的“简单”问题,搞抽象的”复杂“起来,将非常有助于在复杂的直白问题时,”简单“的用抽象的方法实现。