《The C Programming Language, 2nd Edition》K&R Chapter 4 - Functions and Program Structure 笔记

编译

UNIX中cc命令cc main.c getline.o strindex.o将main.c重新编译,之后与getline.o、strindex.o一起添加到可执行文件a.out中。

外部变量和函数

C程序可以看作由一系列外部对象组成,外部对象可以是函数或变量。外部指函数外,内部指函数内。
因为C语言不允许在函数内定义函数,所以函数本身是外部的。

没有声明原型的函数,在第一次被调用时,隐式声明为返回int。

同一个名字的所有外部对象的所有引用,都指向同一个对象,即外部链接。
外部对象的作用域从声明开始到文件末尾。

使用在其他源文件中的外部对象时,必须声明为extern。当声明时存储类别(Storage Class)缺省,外部对象隐式声明为extern。
extern声明不需要指明数组的长度。

static声明的外部对象为静态、内部链接,对其他源文件不可见,避免了名称冲突。
static声明的内部变量为静态、无链接,只能在函数中被访问(与自动变量相同),之后一直占据存储空间不随函数退出消失(与自动变量不同)。

register

register声明表示该变量使用频率较高,建议编译器将变量放在寄存器中(程序更小,速度更快),但编译器可以选择无视。
register声明只适用于自动变量和函数形参。
无论register声明的变量是否最终放在寄存器中,它的地址都是不可访问的。

初始化

block内声明的自动变量,每次进入block时都初始化,离开block时消失。
block内声明的静态变量,第一次进入block时初始化。
block内声明block外存在的变量,会隐藏block外的变量,声明新的变量。

不进行显式初始化的情况下,静态变量都初始化为0,自动变量和寄存器变量是未定义的。
静态变量的初始化表达式必须是常量表达式,且只初始化一次(在程序开始前,或第一次进入block时)。
自动变量与寄存器变量的初始化表达式任意,每次进入函数或block时都被初始化。

数组声明时允许后跟一个初始化表达式列表进行初始化。当省略数组长度时,长度为列表中初始化表达式的个数。当初始化表达式的个数比数组长度小时,则初始化为0。如果初始化表达式的个数大于数组长度是错误的。
字符数组允许以一个字符串替代初始化表达式列表,数组元素包含字符串末尾的'\0'。

递归

递归不节省内存,执行速度也不快,但代码紧凑,更容易编写和理解。

预处理

#include如果文件名用引号,则表示先从源文件位置查找该文件,如果没找到则按文件名用尖括号相同的规则查找该文件,具体规则取决于具体实现。
在大程序中用#include将所有声明放在一起是比较好的方法,保证了所有源文件使用相同的声明。缺点是当内容发生变化时,所有依赖于这个文件的源文件都必须重新编译。

#define定义的名字,作用域从定义位置起到文件末尾。太长的宏定义可以使用「\」换行。
#define允许带参数,如#define max(A, B) ((A) > (B) ? (A) : (B))
将函数定义成宏,可以减少函数调用的开销。
#undef取消#define定义的名称。
「#」:#define dprint(expr) printf(#expr " = %g\n", expr)dprint(x/y);等价于printf("x/y = %g\n", x/y);
「##」:#define paste(front, back) front ## backpaste(name, 1);等价于变量名name1。

#elif类似于else if。
#if define(名字),当名字定义时,值1,否则为0。等价于#ifdef。#if !define(名字)等价于#ifndef。

例程

找出所有包含指定字符串的行

#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */

int getline(char line[], int max);
int strindex(char source[], char searchfor[]);

char pattern[] = "ould"; /* pattern to search for */

/* find all lines matching pattern */
main()
{
    char line[MAXLINE];
    int found = 0;

    while (getline(line, MAXLINE) > 0)
        if (strindex(line, pattern) >= 0) {
            printf("%s", line);
            found++;
        }
    return found;
}

/* getline: get line into s, return length */
int getline(char s[], int lim)
{
    int c, i;
    i = 0;
    while (--lim > 0 && (c=getchar()) != EOF && c != '\n')
        s[i++] = c;
    if (c == '\n')
        s[i++] = c;
    s[i] = '\0';
    return i;
}

/* strindex: return index of t in s, -1 if none */
int strindex(char s[], char t[])
{
    int i, j, k;
    for (i = 0; s[i] != '\0'; i++) {
        for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++)
            ;
        if (k > 0 && t[k] == '\0')
            return i;
    }
    return -1;
}

atof函数(将字符串转换为浮点)

#include <ctype.h>

/* atof: convert string s to double */
double atof(char s[])
{
    double val, power;
    int i, sign;

    for (i = 0; isspace(s[i]); i++) /* skip white space */
        ;
    sign = (s[i] == '-') ? -1 : 1;
    if (s[i] == '+' || s[i] == '-')
        i++;
    for (val = 0.0; isdigit(s[i]); i++)
        val = 10.0 * val + (s[i] - '0');
    if (s[i] == '.')
        i++;
    for (power = 1.0; isdigit(s[i]); i++) {
        val = 10.0 * val + (s[i] - '0');
        power *= 10;
    }
    return sign * val / power;
}

atoi函数(将字符串转换为整数)

/* atoi: convert string s to integer using atof */
int atoi(char s[])
{
    double atof(char s[]);
    return (int) atof(s);
}

逆波兰计算器

(1 - 2) * (4 + 5)逆波兰表示法为1 2 - 4 5 + *

calc.h

#define NUMBER '0' /* signal that a number was found */

void push(double);
double pop(void);
int getop(char []);
int getch(void);
void ungetch(int c);

main.c

#include <stdio.h>
#include <stdlib.h> /* for atof() */
#include "calc.h"

#define MAXOP 100 /* max size of operand or operator */

/* reverse Polish calculator */
main()
{
    int type;
    double op2;
    char s[MAXOP];

    while ((type = getop(s)) != EOF) {
        switch (type) {
        case NUMBER:
            push(atof(s));
            break;
        case '+':
            push(pop() + pop());
            break;
        case '*':
            push(pop() * pop());
            break;
        case '-':
            op2 = pop();
            push(pop() - op2);
            break;
        case '/':
            op2 = pop();
            if (op2 != 0.0)
                push(pop() / op2);
            else
                printf("error: zero divisor\n");
            break;
        case '\n':
            printf("\t%.8g\n", pop());
            break;
        default:
            printf("error: unknown command %s\n", s);
            break;
        }
    }
    return 0;
}

getop.c

#include <stdio.h>
#include <ctype.h>
#include "calc.h"

/* getop: get next character or numeric operand */
int getop(char s[])
{
    int i, c;

    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;
    s[1] = '\0';
    if (!isdigit(c) && c != '.')
        return c; /* not a number */
    i = 0;
    if (isdigit(c)) /* collect integer part */
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') /* collect fraction part */
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';
    if (c != EOF)
        ungetch(c);
    return NUMBER;
}

stack.h

#include <stdio.h>
#include "calc.h"

#define MAXVAL 100 /* maximum depth of val stack */

int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */

/* push: push f onto value stack */
void push(double f)
{
    if (sp < MAXVAL)
        val[sp++] = f;
    else
        printf("error: stack full, can't push %g\n", f);
}

/* pop: pop and return top value from stack */
double pop(void)
{
    if (sp > 0)
        return val[--sp];
    else {
        printf("error: stack empty\n");
        return 0.0;
    }
}

getch.c

#include <stdio.h>

#define BUFSIZE 100

char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */

int getch(void) /* get a (possibly pushed-back) character */
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c) /* push character back on input */
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

打印十进制整数

#include <stdio.h>

/* printd: print n in decimal */
void printd(int n)
{
    if (n < 0) {
        putchar('-');
        n = -n;
    }
    if (n / 10)
        printd(n / 10);
    putchar(n % 10 + '0');
}

快速排序

/* qsort: sort v[left]...v[right] into increasing order */
void qsort(int v[], int left, int right)
{
    int i, last;
    void swap(int v[], int i, int j);
    
    if (left >= right) /* do nothing if array contains */
        return; /* fewer than two elements */
    swap(v, left, (left + right)/2); /* move partition elem */
    last = left; /* to v[0] */
    for (i = left + 1; i <= right; i++) /* partition */
        if (v[i] < v[left])
            swap(v, ++last, i);
    swap(v, left, last); /* restore partition elem */
    qsort(v, left, last-1);
    qsort(v, last+1, right);
}

/* swap: interchange v[i] and v[j] */
void swap(int v[], int i, int j)
{
    int temp;

    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}