12.1 指针的算术运算
说明:指针不仅可以指向普通变量,还可以指向数组元素。
1 | int a[10], *p; |
3种格式:
1 | p = &a[2]; |
12.1.2 指针减去整数
说明:如果p
指向数组元素a[i]
,那么p-j
指向a[i-j]
。
1 | p = &a[8]; |
12.1.3 指针相减
说明:当两个指针相减时,结果为指针之间的距离。
用途:用来计算数组中元素的个数。如果p
指向a[i]
且q
指向a[j]
,那么p-q
就等于i-j
。
限制:
- 只有在
p
指向数组元素时,指针p
上的算数运算才会获得有意义的结果 - 只有在两个指针指向同一个数组时,指针相减才有意义
1 | p = &a[5]; |
12.1.4 指针比较
说明:可以用关系运算符(<
<=
>
>=
)和判等运算符(==
和!=
)。
限制:只有在两个指针指向同一个数组时,用关系运算符进行的指针比较才有意义。
1 | p = &a[5]; |
12.2 指针用于数组处理
说明:指针的算术运算允许通过对指针变量进行重复自增来访问数组的元素。
注意:数组a
的下标是0到N-1
,但&a[N]
是合法的,只要不尝试对该地址存储空间进行读取或写入,就是安全的。
性能:一些编译器依赖下标而不是指针产生的循环代码性会更好。
1 |
|
*
运算符和++
运算符的组合
表达式 | 含义 |
---|---|
*p++ 或*(p++) |
表达式的值是*p ,然后p 自增1 |
(*p)++ |
表达式的值为*p ,然后*p 自增1 |
*++p |
先自增p ,再取自增后的指针对应的值 |
++*p 或++(*p) |
表达式的值是*p 自增后的值 |
1 | p = &a[0]; |
程序:栈
1 |
|
12.3 用数组名作为指针
说明:可以用数组的名字作为指向数组第一个元素的指针。
意义:简化了指针的算术运算,而且使得数组和指针都更加通用。
局限:虽然可以把数组名用作指针,但是不能给数组名赋新的值。试图使数组名指向其它地方是错误的。所以当需要的时候,可以先将数组指针复制给其它变量,然后改变该指针变量。
1 | int a[10]; |
12.3.1 程序:数列反向(改进版)
1 | /** |
1 | $ ./reverse2 |
12.3.2 数组型实际参数(改进版)
说明:数组作为实参传递给函数是是作为指针传递的(传引用),而不会复制数组。
因为没有对数组进行复制,所以数组作为实际参数不会防止原数组被修改
防改变:为了指明数组形式参数不会改变,可以在它的声明中包含单词const
给函数传递数组所需的时间不依赖于数组的大小
- 声明形参时,数组型式和指针形式等价,编译器处理这两类声明就好像它们是完全一样的
局限:不适用于普通变量的声明
1 | int a[10];//会导致编译器为10个整数预留空间 |
- 可以给形式参数为数组的函数传递数组的“片段”
1 | int find_largest(int a[], int n){ |
12.3.3 用指针作为数组名
说明:指向数组的指针除了可以通过指针的方式操作数组外,还可以将该指针当作数组来使用。
1 |
|
12.4 指针和多维数组
12.4.1 处理多维数组的元素
说明:可以通过指针递增的方式访问到多维数组的每一个元素,因为c语言始终按照顺序存储多维数组的元素。
数组遍历(数组下标的方式)
1 | int row, col; |
数组遍历(指针的方式)
1 | int *p; |
12.4.2 处理多维数组的行
说明:二维数组第i
(从0开始)行第一个元素的地址a[i]
== *(a + i)
==&a[i][0]
== &(*(a[i] + 0))
== &*a[i]
1 | int a[NUM_ROWS][NUM_COLS], *p, i; |
12.4.3 用多维数组名作为指针
指针的指针:int a[10], b[10][10];
a
可以看作是int *
型的指针,而b
用作指针时则是int **
型的(指针某个整数的指针的指针)。