指针指向数组元素时,C语言允许对指针进行算术运算(加&减)。由此,可用指针代替数组下标来处理数组。
C语言支持的3种指针算术运算:
- 指针加上整数;
- 指针减去整数;
- 两指针相减。(不能相加)
指针能与整数加减的原理是该整数会根据指针类型进行缩放,类似int与doule类型想加减。
1、指针加上整数
指针 p 加上整数 j 后,指向 p 的对象后的第 j 个位置,即:
若 p
→ a[i]
则 p + j
→ a[i+j]
以上成立的前提是:
i
,j
均为整型;a[i]
,a[i+j]
均存在。
2、指针减去整数
指针 p 减去整数 j 后,指向 p 的对象前的第 j 个位置,即:
若 p
→ a[i]
则 p - j
→ a[i-j]
以上成立的前提是:
i
,j
均为整型;a[i]
,a[i-j]
均存在。
注意:不允许整数减去指针。
3、两指针相减
两指针相减,结果为指针之间的距离(数组元素对象的标号之差)。即:
若 p
→ a[i]
且 q
→ a[j]
则 p - q = i-j
举例:
int a[] = {0, 1, 2, 3, 4, };
int i, *p = &a[5], *q = &a[1];
i = p - q;
printf(" p = %p\n", p);
printf(" q = %p\n\n", q);
printf("%%d : i = %d\n", i);
printf("%%d : p-q = %d\n", p-q); // %d输出
printf("%%X : p-q = %X\n", p-q); // %X输出
printf("%%p : p-q = %p\n", p-q); // %p输出
执行结果:
注意:
- 在不指向任何数组元素的指针进行算术运算会导致未定义行为。
- 两指针指向同一个数组,指针相减才有意义。
4、指针比较
指针比较:>
,<
,>=
,<=
,==
,!=
只有指向同一数组时,指针比较才有意义。
p = &a[5];
q = &a[1];
对于以上两指针,p<=q 的值是0,p>=q 的值是1。
5、指向复合字面量的指针
复合字面量:指定内容元素的没有名字的数组。(见 【07 函数】)
举例:
int *p = (int []){1, 2, 3, 4, 5}; //p指向数组的第一个元素
此语句等价于:
int a[] = {1, 2, 3, 4, 5};
int *p = &a[0];
二、指针处理数组 1、使用指针 依序访问数组元素
#define N 10
...
int a[N], sum, *p;
...
sum = 0;
for(p=&a[0]; p<&a[N]; p++){
sum += *p;
}
- 使用数组的地址来控制循环;
- a[N]不存在,&a[N]是合法的。程序不会检查a[N]的值。当p = &a[N]时,循环结束。
2、
*
运算符与++
运算符的组合
(1)举例
举例1: 把值存入数组元素后,转到下一个元素
a[i++] = j; //方法1
*p++ = j; //方法2
说明:
i++
表达式的值是自增之前的值;- 后辍
++
运算符的优先级高于*
运算符。等价于*(p++) = j;
自增表达式含义举例:
表达式 | 含义 |
---|---|
*p++ 或*(p++) | 自增前表达式的值是*p,之后再自增p |
(*p)++ | 自增前表达式的值是p,之后再自增p |
*++p 或*(++p) | 先自增p,自增后表达式的值是*p |
++*p 或++(*p) | 先自增p,自增后表达式的值是p |
*p++
的应用最常见。
for(p=&a[0]; p<&a[N]; p++){
sum += *p;
}
等价于:
p = &a[0];
while(p<&a[N]){
sum += *p++;
}
三、数组名作为指针 1、概念
使用数组名作为指向数组的第一个元素的指针,且该指针不需要单独声明。
int a[10];
*a = 7; // a[0]=7; 不需要单独声明
*(a+1) = 12; // a[1]=12
说明:
- 对C编译器而言,a[i]与*(a+i)等价,a+i 与 & a[i] 等价。
- 把数组的取下标 *** 作视为指针的算术运算。
2、数组作为函数参数 (1)形式参数
形参参数声明中,*a 与 a[ ] 等同。
int find_largest(int a[], int n)
{
...
}
//上下两种函数的声明是相同的,都是将指针作为形式参数
int find_largest(int *a, int n)
{
...
}
注意:当作为形式参数的数组为const
时,对应的指针也需要加const
int find_largest(const int a[], int n)
{
int *p = a; //错误写法
const int *p = a; //正确写法
}
(2)实际参数
数组名在传递给函数时,也总是被视为指针(被转换为指针)。
注意:数组名在需要时被编译器视为指针,但本身并不是指针,例如sizeof(a)计算的是数组总长度,而非指针类型的长度。
//函数定义
int find_largest(int a[], int n)
{
...
}
//函数调用,传入数组
find_largest(b, N); //数组名b作为实际参数传入
结果是把 &b[0] 赋给a,数组并未被赋值。
数组型形式参数视作指针的优缺点:
- 缺点
- 与变量不同,传入函数后,数组本身没有被复制,因而作为实际参数的数组可能被修改。为了避免被修改,可在函数声明中使用const。如:
int find_largest(const int a[], int)
保护作为实际参数的数组。
- 与变量不同,传入函数后,数组本身没有被复制,因而作为实际参数的数组可能被修改。为了避免被修改,可在函数声明中使用const。如:
- 优点
- 给函数传递数组的时间与数组大小无关;
- 可以把数组型实际参数声明为指针。例如:
int find_largest(int *a, int n)
,在函数参数表中,声明形式参数a为指针相当于声明它是数组。 - 可以只传递数组的一部分,可以不用全部传入。如
largest = find_largest(&b[5], 10);
函数直接从b[5]开始检查此后的10个元素,而不必检查全部。
3、指针作为数组名
int a[5], i, sum = 0;
int *p = a; //p指向a[0]
for(i=0; i<5; i++){
sum += p[i]; //指针p被视为数组名a,p[i]视为*(p+i)
}
4、应用
(1)遍历数组
数组元素求和:
int a[N];
for(p = a; p < a+N; p++){
sum += *p;
}
注意:
- 数组名作为指针时,不得修改该指针的值(非法)。
- 例如
int a[]; a++;
是错误代码。可以将a复制给另一个非数组名p的指针再修改p的值。
数组元素逆序输出:
for(p=a+N-1; p>a; p--){
printf(" %d", *p); //只逆序输出,未逆序存储
}
printf("\n");
(2)数组元素逆序存储
见此文第二章第2节【C语言补充】数组元素逆序(反向)
四、指针与多维数组 1、处理多维数组的元素
处理方法:对于二维数组而言,可以将二维数组当作一维数组来处理。
r 行数组的存储方式:
举例: 对二维数组所有元素初始化为0
int a[row][col];
int *p;
//正确写法1:
for(p=&a[0][0]; p<= &a[row-1][col-1]; p++){
*p = 0;
}
//正确写法2:
for(p=a; p<= &a[row-1][col-1]; p++){
*p = 0;
}
//错误写法1:元素越界,不存子啊a[row][]和a[][col]
for(p=a; p<= &a[row][col]; p++){
*p = 0;
}
//错误写法2:原因见本章第4节
for(p = a; p < a+ROW*COL; p++){
*p = 1;
}
注意: 与一维数组不同,不存在a[ROW][]
和a[][COL]
,因此不能写成p < a[ROW][COL]
2、处理多维数组的行 (1)概念
p = &a[i][0]; //访问第i行第1个元素
或简写为:
p = a[i]; //访问第i行第一个元素
(2)应用:对第 i 行清零说明:
- 对于任意数组a而言,
a[i]
等价于*(a+i)
。则&(a[i][0])
等价于&( *(a[i]+0) )
,又等价于&( *a[i] )
,等价于a[i]
。- 即,对于数组,a是指向数组a的第一个元素的指针。对于二维数组
a[i]
是指向第i
行的第一个元素的指针。
//对数组第i行清零
int a[ROW][COL], i;
int *p;
...
for(p=a[i]; p<a[i]+COL; p++){
*p = 0;
}
注意:对于二维数组a[ROW][COL]
,a[0]
指向第0行的首个元素。
3、处理多维数组的列
//对数组第j列清零
int a[ROW][COL], j;
int (*p)[COL]; //p声明为指向该数组整行的指针
...
for(p=&a[0]; p< &a[ROW] ; p++){
(*p)[j] = 0; //每行的第j个元素赋0
}
说明:
*p[COL]
等价于*(p[COL])
是指针数组,而(*p)[COL]
是指向数组(长度为COL)的指针。p++;
将指针移到下一行的开头。(*p)[i]
代表a的一整行。
4、用多维数组作为指针 (1)概念
- 对于一维数组,a是指向
a[0]
的指针。 - 对于二维数组,a是指向
a[0]
(第0行),而a[0]
才是指向a[0][0]
的指针。
由行主序存储原则,C语言视其为一维数组(长度为行数),数组的每个元素也是一维数组(长度为列数)。
(2)应用:将二维数组第 j 列清零
- 对于
int a[n]
,a的类型是int *
(指向整型变量的指针);- 对于
int a[ROW][COL]
,a的类型是int (*)[COL]
(指向长度为COL的整型数组的指针,也即指向数组某一行的指针)。
//方法1:
for(p=&a[0]; p< &a[ROW] ; p++){
(*p)[j] = 0; //每行的第j个元素赋0
}
//方法2(简化):
for(p=a; p< a + ROW ; p++){
(*p)[j] = 0; //每行的第j个元素赋0
}
(3)多维数组作为函数实际参数
对于找数组最大值的函数find_largest(int a[], int n)
:
错误写法:
largest = find_largest(a, ROW*COL); //a不是int *类型,与参数表要求不符
largest = find_largest(a[0][0], ROW*COL); //a[0][0]不是int *类型
正确写法:
largest = find_largest(a[0], ROW*COL); //a[0]指向第0行的首个元素
五、指针与变长数组 1、一维变长数组
void f(int n)
{
int a[], *p;
p = a;
...
}
p指向一维数组中的元素
2、二维变长数组void f(int m, int n)
{
int a[m][n], (*p)[n];
p = a;
...
}
- 对于多维变长数组,指针类型取决于除第一维之外的其他每一维的长度。
- 因为p依赖于n,而n是变量,则p具有变量修改类型。
- 变量修改类型的声明必须出现在函数体内部或函数原型中。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)