C程语言序模式

一个语言的程序设计有一些常用的基本模式,这些模式是许多人长期工作的总结。在这个语言的程序中到处可见。对于一些典型问题,采用适当的模式是最容易把程序写好、写正确的。在这里我们将从简单到复杂,分门别类地列处一些C程序模式,供大家参考。这里列出的东西选自各种材料(包括《从问题到程序》书中)。这里列出的东西是很初步的。如果你认为有些东西也很重要,请给我们提示。

在所有模式描述中,用$$符号括起来的一段段文字表示的是应该实际写出的东西。

有关解释如果写了页数,请参考《从问题到程序》书中相关的页。



简单输出程序模式:

#include <stdio.h>

main () {
   $一个或几个输出语句,例如printf("Hello, world!\n");$
}

----------------------------------------------------
例:

#include <stdio.h>

main() {
    printf("Welcome\n");
    printf("to\n");
    printf("Beijing!\n");
}



简单表达式计算模式:

#include <stdio.h>

main() {
    printf($格式描述串$, $一个或几个表达式$);
}

---------------------------------------------------

注意:格式描述串中转换描述与参数个数一致,类型一致。

---------------------------------------------------

#include <stdio.h>

main() {
    printf("%f * %f = %f", 3.14, 6.5, 3.14 * 6.5);
}



简单算术计算函数定义:

double fun ($一个或几个参数的说明$) {
    return $计算表达式$;
}

----------------------------------------------------
1,函数返回值类型根据需要写出,未必是double
2,参数写出类型和参数名的对,多个参数用逗号分隔;
3,计算表达式描述如何由参数出发算出函数值;
4,如果计算中需要用数学函数,应该在程序开始写一行

    #include <math.h>

----------------------------------------------------
例:

double circle_area (double r) {
    return r * r * 3.14159265;
}



有函数定义的一般程序

#include

…… /* 函数定义写在这里(可以有一个或几个) */

main () {

    …… /* 主程序体,通常包含对函数的调用 */

}



常用while循环形式

n = 1; /* 初始化循环中使用的变量 */

while (n <= 100) { /* 已知次数的循环用for结构写得更多 */

    /* 其他语句 */

    n = n + 1; /* 循环最后更新辅助变量 */
}

注:这种更常见的是采用for循环形式写。见下。
----------------------------------------------------

/* 有关变量的初始化 */

while ($表达式$ >= 1E-6) {

    /* 循环中的计算应该影响“表达式”的值,否则将导致无限循环 */

}



常用for循环形式

for (n = 0; n < N; n++) {
    $循环体里的语句$
}

---------------------------------------------------

注意C语言里一般不用(除非有特殊目的):

for (n = 1; n <= N; n++) {

    $循环体里的语句$

}



给程序计时:

#include <stdio.h>
#include <time.h>

/* 其他定义 */

main () {
    /* 其他程序变量的定义 */
    double x;

    x = clock() / CLOCKS_PER_SEC;

    /* 需要计时的程序片段 */

    x = clock() / CLOCKS_PER_SEC - x;
    printf("Timing: %f\n", x); /* 输出形式自己确定 */

}

---------------------------------------------------

注意:不同系统的计时精度可能不同。有些系统还有非标准的计时功能。



输入和处理由标准输入文件来的一系列字符

int c;

/* 其他定义或语句 */

while ((c = getchar()) != EOF) {

    /* 对读入字符的处理 */

}

---------------------------------------------------

1,接收读入字符的变量(例如上面的c)必须用int类型;
2,注意while条件中的括号,不能写错;
3,应该写 #include <stdio.h>



最常见的数组操作
 

for (i = 0; i < N; i++) {
    .... ....
    .... a[i] .... b[i]
    .... ....

}

---------------------------------------------------

1,循环范围不能超出数组下标的范围



典型的处理数组的函数

double sqsum(double a[], int n) {
    double x = 0.0;
    int i;
    for (i = 0; i < n; i++)
        x += a[i] * a[i];
    return x;
}

---------------------------------------------------

1,注意数组参数的写法;

2,增加一个描述数组长度的参数,借助于它控制对数组元素的处理;

3,通过这种函数可以改变实际参数数组。



处理字符串的典型函数

void str_copy (char s[], char t[]) {
    int i;
    for (i = 0; t[i] != '\0'; i++)
        s[i] = t[i];
    s[i] = '\0';
}

---------------------------------------------------

void str_copy1 (char *s, char *t) {
    for (; *t != '\0'; s++, t++)
        *s = *t;
    *s = '\0';
}

---------------------------------------------------

1,参数用 char s[] 或者 char *s 描述,两者等价,但用 char s[] 更具有提示性;
2,用遇到空字符'\0'作为循环结束条件;
3,还有些更紧凑的写法,参见有关书籍



逐个处理命令行参数

int main (int argc, char *argv[]) {
    int i;
    ... ...
    for (i = 0; i < argc; i++) {
        .... argv[i] ....
    }
    ... ...

}

---------------------------------------------------

int main (int argc, char *argv[]) {
    ... ...
    for (; *agrv != NULL; argv++) {
        ... *argv ...
    }
    ... ...
}

---------------------------------------------------
循环中的 *argv 将依次取各个命令行参数字符串。

注意:编号0的参数是命令名本身,如果不处理它应该先跳过去。



使用动态存储分配函数malloccalloc
 

if ((p = (... *)malloc(...)) == NULL) {
   .. ... /* 对分配未成功情况的处理 */
}

---------------------------------------------------

1,指针应当具有合适的类型;
2,在malloc名前写合适的转换描述;
3malloc的参数应当借助sizeof计算。



链接表的处理模式(以扫描的方式逐个处理表中的数据)
 

for (p = head; p != NULL; p = p->next) {
    .... p->data ....

}

---------------------------------------------------

假设head是指向表头结点的指针。
结点中的next成分是指向下一个结点的指针,data是数据。



文件使用的基本模式
 

FILE *fp;
.... ....

if ((fp = fopen($文件名$, $打开方式$)) == NULL) {
    /* 当文件打不开时的处理 */
}

... ... /* 对文件的各种操作 */

fclose(fp);

---------------------------------------------------

执行文件打开操作之后,必须检查操作是否正常完成。



使用<stdlib.h>里的bsearchqsort,比较函数的基本写法

1,确定被比较对象的顺序关系;
2,比较函数总用两个const void* 参数;
3,在函数开始时将参数正确转换到指向数组元素类型的指针;
4,在第一个参数大于、等于、小于第二个时分别返回10-1
5,注意使用方式。

---------------------------------------------------

例子:假定被排序的是整数数组。定义比较函数:

int icmp(const void *p, const void *q){
    const int *m = p, *n = q;
    return *m > *n ? 1 : (*m == *n ? 0 : -1);
}

使用:

int *p, a[] = {5, 6, 3, 28, 23, 34, 7, 9, 6, 14}, k = 7;

int main () {
    ... ...
    qsort(a, sizeof(a)/sizeof(int), sizeof(int), icmp);
    /* 这样,数组a的元素已经按照上升顺序排列好了 */

    p = bsearch(&k, a, sizeof(a)/sizeof(int), sizeof(int), icmp);
    /* 指针p将指向数组a中元素7的位置 */

    ... ...

}

---------------------------------------------------

请注意在这里如何为bsearchqsort提供所要求的各个参数。