ANSI C 使用 void* 作为通用指针类型.

5.1 指针与地址

指针 p 指向 char 类型的 c:

image.png

  1. p = &c;
  • & 只能作用于内存中的对象, 即变量和数组元素, 不能作用于表达式, 常量或 register 类型的变量.
  • * 间接寻址或间接引用运算符

image.png

int *ip; // 强调 *p 的结果是 int
double *dp, atof(char *); // *dp 和 atof() 都是 double 类型

void * 不能间接引用自身.

iq = ip // iq 与 ip 指向相同的地方

5.2 指针与函数参数

void swap(int *px, int *py) {
    int temp;

    temp = *px;
    *px = *py;
    *py = temp;
}

image.png

image.png
image.png

5.3 指针与数组

int a[10];

image.png

int *pa;
pa = &a[0];

image.png

*(pa + i)

image.png

int a[3];
int *p;
p = a; // 合法

int b;
b = *(a+1); // 合法

int c;
c = p[1]; // 合法

p++; // 合法
a = p; // 非法
a++ // 非法

在函数定义中, char s[]char *s 等价.

可以利用地址传递子数组给函数:

f(&a[2]);
f(a+2);

如果确信元素在数组中存在, 也可以使用负数:

p[-1];

5.4 地址算术运算

#define ALLOCSIZE 10000

static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;

char *alloc(int n) {
    if (allocbuf + ALLOCSIZE - allocp >= n) {
        allocp += n;
        return allocp - n; // 被分配内存的起始地址
    } else {
        return 0;
    }
}

void afree(char *p) {
    if (p >= allocbuf && p < allocbuf + ALLOCSIZE) {
        allocp = p;
    }
}

image.png

int *p; // 此时 p == 0;

指针不能与整数相互转换, (除了0).

  • 指针可以与常量0进行比较
  • C 中的 NULL 定义在

在计算 p+n 时, n 将根据 p 指向的对象的长度按比例缩放, p 指向的对象的长度取决于 p 的声明.

总结:

image.png

5.5 字符指针与函数

char *pmessage;
pmessage = "now is the time";

字符指针与字符数组的区别:

#include <stdio.h>

int main() {
    char a[] = "abc";
    a = "def"; // 编译错误
    char *p = "abc";
    p = "def";
    p[0] = 'g'; // 运行时错误

    printf("%s\n", p);
    printf("%s\n", p[1]); // 编译时警告, 运行时错误
    printf("%c\n", p[1]); // %c
    printf("%c\n", *(++p));
    return 0;
}

image.png

赋值字符串的精简函数:

void strcpy(char *s, char *t) {
    while(*s++ = *t++)
        ;
}

字符串比较的精简函数:

int strcmp(char *s, char *t) {
    // 只要 s[i] != t[i], 循环就退出了,
    // 不用担心 len(s) != len(t)
    for (; *s == *t; s++, t++) {
        if (*s == '\0') {
            return 0;
        }
    }
    return *s - *t;
}

字符栈操作的精简写法:

*p++ = val; // 入栈
val = *--p; // 出栈

5.6 指针数组以及指向指针的指针

对文本行排序:

  • 所有文本行存储在一个大字符串数组中
  • 交换的是每个文本行的指针
  • 这些指针存储在数组中 (指针数组)

image.png

image.png
image.png
image.png
image.png
image.png

5.7 多维数组

image.png
image.png

逻辑表达式的结果只可能是0或1.

声明需要二维数组参数的函数的方式:

  • 必须指明列数
f(int daytab[2][13]) {}
f(int daytab[][13]) {}
// [] 的优先级比 * 高, 所以使用 ()
// int *daytab[13] 指向整型对象的指针
f(int (*daytab)[13]) {} // 指向一维数组

5.8 指针数组的初始化

编译器确定数组长度:

image.png

5.9 指针与多维数组

int a[10][20]; // 分配了200个空间
int *b[10];    // 分配了10个指针

矩阵下标公式:

20*row + col

指针数组的优点:

  • 每行所具有的元素数可以不同

字符串指针数组:

image.png

二维数组:

image.png

注意存储形式.

5.10 命令行参数

image.png

image.png

image.png
image.png

format 字符串可以是表达式:

printf((argc > 1) ? "%s " : "%s", *++argv);

image.png

添加命令行 flag:

  • [] 的优先级比 * ++ 都高
  • * ++ 的优先级相同

image.png

5.11 指向函数的指针

排序通常包括3个部分:

  • 比较
  • 交换
  • 算法

image.png

image.png
image.png

错误的写法:

int *comp(void *, void *)

image.png

image.png

5.12 复杂声明

C 语言的声明不能从左至右阅读.

// 函数声明, 返回 int *
int *f();

// 指针声明, 指向函数, 返回 int
int (*pf)();