C语言模块化编程重构实战

2016-12-06 10:59:05来源:作者:工学1号馆人点击

第七城市

在《C语言模块化编程》中介绍了C语言模块化编程的一些规则和模板,本文通过使用模块化编程重构C代码的实战,进一步深入理解C模块化编程。

本文地址: http://wuyudong.com/2016/12/05/3136.html,转载请注明出处。

首先看一个完整的C程序:

#include <stdio.h>#include <string.h>int main(){ char *strings[128]; char string[256]; char *p1, *p2; int nstrings; int found; int i, j; nstrings = 0; while (fgets(string, 256, stdin)) { for (i = 0; i < nstrings; i++) { found = 1; for (p1 = string, p2 = strings[i]; *p1 && *p2; p1++, p2++) { if (*p1 > *p2) { found = 0; break; } } if (found) break; } for (j = nstrings; j > i; j--) strings[j] = strings[j - 1]; strings[i] = strdup(string); nstrings++; if (nstrings >= 128) break; } for (i = 0; i < nstrings; i++) fprintf(stdout, "%s", strings[i]); return 0;}

初次见到这个代码的时候,你能说出这个程序表示什么意思吗?

如果写成下面这样,会不会好些?

#include <stdio.h>#include <string.h>#define MAX_STRINGS 128#define MAX_STRING_LENGTH 256void ReadStrings(char **strings, int *nstrings, int maxstrings, FILE *fp){ char string[MAX_STRING_LENGTH]; *nstrings = 0; while (fgets(string, MAX_STRING_LENGTH, fp)) { strings[(*nstrings)++] = strdup(string); if (*nstrings >= maxstrings) break; }}void WriteStrings(char **strings, int nstrings, FILE *fp){ int i; for (i = 0; i < nstrings; i++) fprintf(fp, "%s", strings[i]);}int CompareStrings(char *string1, char *string2){ char *p1 = string1; char *p2 = string2; while (*p1 && *p2) { if (*p1 < *p2) return -1; else if (*p1 > *p2) return 1; p1++; p2++; } return 0;}void SortStrings(char **strings, int nstrings){ int i, j; for (i = 0; i < nstrings; i++) { for (j = i + 1; j < nstrings; j++) { if (CompareStrings(strings[i], strings[j]) > 0) { char *swap = strings[i]; strings[i] = strings[j]; strings[j] = swap; } } }}int main(){ char *strings[MAX_STRINGS]; int nstrings; ReadStrings(strings, &nstrings, MAX_STRINGS, stdin); SortStrings(strings, nstrings); WriteStrings(strings, nstrings, stdout); return 0;}

上面的代码将开始代码中的循环部分拆分成若干函数,这样结构上清晰很多,而且很容易看出代码的作用其实是升序排序所输入的字符串

模块化

将执行分解到模块:

Readstrings、Sortstrings、Writestrings

接口隐藏细节--使变化的影响在局部

为什么后面的代码好?

(1)容易理解

int main(){ char *strings[MAX_STRINGS]; int nstrings; ReadStrings(strings, &nstrings, MAX_STRINGS, stdin); SortStrings(strings, nstrings); WriteStrings(strings, nstrings, stdout); return 0;}

只看上面的main函数就知道整个程序的作用

(2)容易测试与debug

上面main函数中的代码执行过程为:输入一系列字符串,接着排序,最后打印到终端显示排序后的

如果想在输入完字符串后整体输出到终端,然后再排序,可以这样

int main(){ char *strings[MAX_STRINGS]; int nstrings; ReadStrings(strings, &nstrings, MAX_STRINGS, stdout); WriteStrings(strings, nstrings, stdout); SortStrings(strings, nstrings); WriteStrings(strings, nstrings, stdout); return 0;}

(3)容易代码复用

如果想实现一个函数,其功能为将两个文件中的内容合并然后输出到终端,可以调用Readstrings、Writestrings函数来实现,具体代码如下:

MergeFiles(FILE * fp1, FILE * fp2){ char *strings[MAX_STRINGS]; int nstrings; ReadStrings(strings, &nstrings, MAX_STRINGS, fp1); WriteStrings(strings, nstrings, stdout); ReadStrings(strings, &nstrings, MAX_STRINGS, fp2); WriteStrings(strings, nstrings, stdout);}

(4)容易扩展

对于下面的代码

int CompareStrings(char *string1, char *string2){ char *p1 = string1; char *p2 = string2; while (*p1 && *p2) { if (*p1 < *p2) return -1; else if (*p1 > *p2) return 1; p1++; p2++; } return 0;}

作用是比较字符串的大小,如果想改成比较两个字符串的长度,很简单:

int StringLength(char *string){ char *p = string; while (*p) p++; return p - string;}int CompareStrings(char *string1, char *string2){ return StringLength(string1) - StringLength(string2);} 单独编译

将字符串数组放进单独的文件:

接口声明: stringarray.h

提供相应的实现: stringarray.c

允许复用:其他程序

stringarray.h

extern void ReadStrings(char **strings, int *nstrings, int maxstrings, FILE *fp);extern void WriteStrings(char **strings, int nstrings, FILE *fp);extern void SortStrings(char **strings, int nstrings);extern int CompareStrings(char *string1, char *string2);

stringarray.c

#include <stdio.h>#include <string.h>#define MAX_STRING_LENGTH 256void ReadStrings(char **strings, int *nstrings, int maxstrings, FILE *fp){ char string[MAX_STRING_LENGTH]; *nstrings = 0; while (fgets(string, MAX_STRING_LENGTH, fp)) { strings[(*nstrings)++] = strdup(string); if (*nstrings >= maxstrings) break; }}void WriteStrings(char **strings, int nstrings, FILE *fp){ int i; for (i = 0; i < nstrings; i++) fprintf(fp, "%s", strings[i]);}int CompareStrings(char *string1, char *string2){ char *p1 = string1; char *p2 = string2; while (*p1 && *p2) { if (*p1 < *p2) return -1; else if (*p1 > *p2) return 1; p1++; p2++; } return 0;}void SortStrings(char **strings, int nstrings){ int i, j; for (i = 0; i < nstrings; i++) { for (j = i + 1; j < nstrings; j++) { if (CompareStrings(strings[i], strings[j]) > 0) { char *swap = strings[i]; strings[i] = strings[j]; strings[j] = swap; } } }}

sort.c

#include “stringarray.h”#define MAX_STRINGS 128int main(){ char *strings[MAX_STRINGS]; int nstrings; ReadStrings(strings, &nstrings, MAX_STRINGS, stdin); SortStrings(strings, nstrings); WriteStrings(strings, nstrings, stdout); return 0;}

Makefile

sort: sort.o stringarray.a cc -o sort sort.o stringarray.asort.o: sort.c stringarray.h cc -c sort.cstringarray.a: stringarray.c cc -c stringarray.c ar ur stringarray.a stringarray.oclean: rm sort sort.o sortarray.a sortarray.o 结构化

我们可以看到在stringarray.h中的字符串数组与其元素的数量使用两个单独的变量存储,可以构建一个结构体将它们放在一起

stringarray.h #define MAX_STRINGS 128struct StringArray { char *strings[MAX_STRINGS]; int nstrings;};extern void ReadStrings(struct StringArray *stringarray, FILE *fp);extern void WriteStrings(struct StringArray *stringarray, FILE *fp);extern void SortStrings(struct StringArray *stringarray); sort.c #include <stdio.h>#include “stringarray.h”int main(){ struct StringArray *stringarray = malloc(sizeof(struct StringArray)); stringarray->nstrings = 0; ReadStrings(stringarray, stdin); SortStrings(stringarray); WriteStrings(stringarray, stdout); free(stringarray); return 0;} 使用typedef stringarray.h #define MAX_STRINGS 128typedef struct StringArray { char *strings[MAX_STRINGS]; int nstrings;} *StringArray_T;extern void ReadStrings(StringArray_T stringarray, FILE *fp);extern void WriteStrings(StringArray_T stringarray, FILE *fp);extern void SortStrings(StringArray_T stringarray); sort.c #include <stdio.h>#include “stringarray.h”int main(){ StringArray_T stringarray = malloc(sizeof(struct StringArray)); stringarray->nstrings = 0; ReadStrings(stringarray, stdin); SortStrings(stringarray); WriteStrings(stringarray, stdout); free(stringarray); return 0;} 不透明指针 stringarray.h typedef struct StringArray *StringArray_T;extern StringArray_T NewStrings(void);extern void FreeStrings(StringArray_T stringarray);extern void ReadStrings(StringArray_T stringarray, FILE *fp);extern void WriteStrings(StringArray_T stringarray, FILE *fp);extern void SortStrings(StringArray_T stringarray); sort.c #include <stdio.h>#include “stringarray.h”int main(){ StringArray_T stringarray = NewStrings(); ReadStrings(stringarray, stdin); SortStrings(stringarray); WriteStrings(stringarray, stdout); FreeStrings(stringarray); return 0;} 抽象数据类型

模块化支持单数据结构上的操作

接口声明操作,而不是数据结构 实现对客户隐藏(封装) 使用编程语言的特征来保证封装

常见的做法

通过模块处理分配和释放数据结构 函数和变量的名字以<模块名> _开始 接口中尽可能多的提供通用性/灵活性 使用void*来实现多态性

上面的代码比较的是string类型的数组,如果是int类型或者float……其他类型呢?是不是要重新写代码?

这里对数组类型进行抽象,具体步骤如下:

ADT - 接口

array.h

#ifndef ARRAY_H#define ARRAY_Htypedef struct Array *Array_T;extern Array_T Array_new(void);extern void Array_free(Array_T array);extern void Array_insert(Array_T array, void *datap);extern void Array_remove(Array_T array, void *datap);extern int Array_getLength(Array_T array);extern void *Array_getKth(Array_T array, int k);#endif

ADT - Client 1

如果是string类型,string_client.c

#include “array.h”#include <stdio.h>int main(){ Array_T array; int i; array = Array_new(); Array_insert(array, (void *)“CS217 ”); Array_insert(array, (void *)“IS ”); Array_insert(array, (void *)“FUN ”); for (i = 0; i < Array_getLength(array); i++) { char *str = (char *)Array_getKth(array, i); printf(str); } A rray_free(array); return 0;}

如果是int类型数组,int_client.c

#include “array.h”#include <stdio.h>int main(){ Array_T array; int one = 1, two = 2, three = 3, i; array = Array_new(); Array_insert(array, (void *)&one); Array_insert(array, (void *)&two); Array_insert(array, (void *)&three); for (i = 0; i < Array_getLength(array); i++) { int *datap = (int *)Array_getKth(array, i); printf(“%d “, *datap); } A rray_free(array); return 0;}

ADT--实现

array.c

#include “array.h”#define MAX_ELEMENTS 128struct Array { void *elements[MAX_ELEMENTS]; int num_elements;};Array_T Array_new(void){ Array_T array = malloc(sizeof(struct Array)); array->num_elements = 0; return array;}void Array_free(Array_T array){ free(array);}void Array_insert(Array_T array, void *datap){ int index = array->num_elements; array->elements[index] = datap; array->num_elements++;} int Array_getLength(Array_T array){ return array->num_elements;}void *Array_getKth(Array_T array, int k){ return array->elements[k];}void Array_remove(Array_T array, void *datap){ int index, i; for (index = 0; index < array->num_elements; index++) if (array->elements[index] == datap) break; if (index < array->num_elements) { for (i = index + 1; i < array->num_elements; i++) array->elements[i - 1] = array->elements[i]; array->num_elements--; }} 总结

模块化是设计好的软件的关键

1、将程序分解成模块

2、提供清晰和易扩展的接口

抽象数据类型

1、模块化支持在数据结构上操作

2、设计良好的接口隐藏实现,但是提供灵活性

优点

编译分离

容易理解

容易测试与debug

容易代码复用

容易扩展

第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台