指针、数组和地址之间的关系
在定义数组时,编译器必须分配基地址和足够的存储空间,以存储数组的所有元素。数组的基地址是在内存中存储数组的起始位置,是数组中第一个元素(下标为0)的地址,因此数组名本身是一个地址即指针值。在访问内存方面,指针和数组几乎相同,区别在于:指针是以地址作为值的变量,而数组名的值是一个特殊的固定地址,可以看作常量指针。
假设给出定义int a[100], *p;
,系统将编号为3000,3002,3004,…的内存字节作为数组元素a[0],a[1],a[2],…,a[99]的地址(假定系统int型变量的长度为2个字节),那么其中内存位置3000是数组a的基地址,也是a[0]的地址。
于是p=a;
和p=&a[0];
是等价的,p=a+1;
和p=&a[1];
亦是等价的。
数组和指针之间的关系如下图:
指针 | 内存地址 | 内存单元 | 数组元素 |
---|---|---|---|
p | 3000 | a[0] | |
p+1 | 3002 | a[1] | |
… | … | ||
… | … | ||
p+i | 3000+2i | a[i] | |
… | … | ||
p+99 | 3198 | a[99] |
对数组元素求和
如果已经对数组a进行了赋值,以下语句对数组元素求和:
sum = 0;
for (p = a; p <= a[99]; p++)
sum += *p;
在循环中,指针变量p
的初值是数组a
的基地址,p连续取值&a[0],&a[1],…,&a[99]。
一般而言,如果i
是int
型变量,那么p+i
就是距地址p
的第i
个偏移,类似地,a+i
是距数组a
的基地址的第i
个偏移,*(a+i)
与a[i]
等价。于是有第二种求和方式:
sum = 0;
for (i = 0; i < 100; i++)
sum += *(a + i);
正如*(a+i)
与a[i]
等价一样,*(p+i)
与p[i]
等价。于是有第三种求和方式:
p = a;
sum = 0;
for (i = 0; i < 100; ++i)
sum += p[i];
下面给出分别使用数组和指针计算数组元素之和的代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
int i, a[10], * p;
long sum = 0;
printf("Enter 10 integers:");
for (i = 0; i < 10; i++)
scanf("%d", &a[i]);
for (i = 0; i < 10; i++)
sum = sum + a[i];
printf("calculated by array, sum=%ld\n", sum);
sum = 0;
for (p = a; p <= a + 9; p++)
sum = sum + *p;
printf("calculated by pointer, sum=%ld\n", sum);
return 0;
}
结果如下:
数组名作为函数的参数
针对代码:
int sum(int a[], int n) //int a[]等价于int *a
{
int i, s = 0;
for (i = 0; i < n; ++i)
s += a[i];
return s;
}
数组形参a实际上是一个指针,当进行参数传递时,主函数传递的是数组a的基地址,数组元素本身不被复制。
假定定义数组int b[100]
,在对其赋值之后,可以调用函数sum()
对b
中的元素作累加。
调用 | 被计算和被返回的内容 |
---|---|
sum(b, 100) | b[0] + b[1] + … + b[99] |
sum(b, 88) | b[0] + b[1] + … + b[87] |
sum(&b[7], k-7) | b[7] + b[8] + … + b[k-1] |
sum(b+7, 2*k) | b[7] + b[8] + … + b[2*k+6] |