11.指针与数组

指针与数组

  • 数组名是数组首元素的地址
flizny == &flizny[0]; // 数组名是该数组首元素的地址

flizny 和&flizny[0]都表示数组首元素的内存地址(&是地址运算符)

  • 在C中,指针加1指的是增加一个存储单元

对数组而言,这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址。

dates +2== &date[2]// 相同的地址
*(dates +2) ==dates[2]// 相同的值

函数、数组和指针

  • ##数组求和##
	total =sum(marbles);// 可能的函数调用
	int sum(int* ar)// 相应的函数定义
	{
	int i;
	int total =0;
	for(i =0;i <10;i++)// 假设数组有10个元素
	total +=ar[i];// ar[i] 与 *(ar + i) 相同
	return total;
	}

数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针。

  • 指针表示法和数组表示法
    int *ar形式和int ar[ ]形式都表示ar是一个指向int的指针。
int sum(int *ar, int n){
// 求和,使用指针作为形参
}
int sum(int ar[], int n){
// 求和,使用数组作为形参;二者等价
}
  • 使用指针形参
int sump(int* start, int* end) //函数定义
{
    int total = 0;
    while (start < end)
    {
        total += *start; // add value to total
        start++;         // advance pointer to next element
    }

    return total;
}

answer = sump(marbles, marbles + SIZE);  //函数调用

指针操作

  1. 赋值
    用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。
int urn[5] = { 100, 200, 300, 400, 500 };
int * ptr1, *ptr2, *ptr3;
ptr1 = urn; // 把一个地址赋给指针
ptr2 = &urn[2]; // 把一个地址赋给指针
  1. 解引用
    *运算符给出指针指向地址上储存的值。
// 解引用指针,以及获得指针的地址
printf("pointer value, dereferenced pointer, pointer address:\n");
printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
  1. 取址
    和所有变量一样,指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。
printf("&ptr1 = %p\n", &ptr1);
  1. 指针与整数相加/相减
    整数会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。
//ptr1 +4与&urn[4]等价
ptr3 = ptr1 + 4;
printf("\nadding an int to a pointer:\n");
printf("ptr1 + 4 = %p, *(ptr1 + 4) = %d\n", ptr1 + 4, *(ptr1 + 4));
  1. 递增指针/递减指针
    递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
ptr1++; // 递增指针
printf("\nvalues after ptr1++:\n");
printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
  1. 指针求差
    可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。
// 一个指针减去另一个指针
printf("\nsubtracting one pointer from another:\n");
printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %td\n", ptr2, ptr1, ptr2 - ptr1);
  1. 比较
    使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。
  • 解引用未初始化的指针
    千万不要解引用未初始化的指针。
int * pt;// 未初始化的指针
*pt = 5; // 严重的错误

pt未被初始化,其值是一个随机值,所以不知道5将储存在何处。
创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。

保护数组中的数据

  • 对形式参数使用const
int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
return total;
}

以上代码中的const告诉编译器,该函数不能修改ar指向的数组中的内容。如果在函数中不小心使用类似ar[i]++的表达式,编译器会捕获这个错误,并生成一条错误信息。

  • const的其他内容
  1. 可以创建const变量、const数组、const指针和指向const的指针。
    指向 const 的指针通常用于函数形参中,表明该函数不会使用指针改变数据:
void show_array(const double *ar, int n);
  1. 把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的;然而,只能把非const数据的地址赋给普通指针:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
const double * pc = rates; // 有效
pc = locked; //有效
pc = &rates[3]; //有效
double * pnc = rates; // 有效
pnc = locked; // 无效,
//const数据的地址不能赋给普通指针,这个规则非常合理。
//否则,通过指针就能改变const数组中的数据。
pnc = &rates[3]; // 有效
  1. 可以声明并初始化一个不能指向别处的指针,关键是const的位置:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates; // pc指向数组的开始
pc = &rates[2]; // 不允许,因为该指针不能指向别处
*pc = 92.99; // 没问题 -- 更改rates[0]的值
  1. 在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * const pc = rates;
pc = &rates[2]; //不允许
*pc = 92.99; //不允许

多维数组

  • ##二维数组中的指针问题#
/* zippo1.c --  zippo info */
#include <stdio.h>
int main(void)
{
    int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };

    printf("zippo = %p,zippo + 1 = %p\n", zippo, zippo + 1);
    //数组名zippo是该数组首元素的地址,所以zippo的值和&zippo[0]的值相同;
    //而zippo[0]本身是一个内含两个整数的数组,
    //所以zippo[0]的值和它首元素(一个整数)的地址(即& zippo[0][0]的值)相同。
    //简而言之,zippo[0]是一个占用一个int大小对象的地址,
    //而zippo是一个占用两个int大小对象的地址;因此,zippo + 1和zippo[0] + 1的值不同。
    printf("zippo[0] = %p,zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1);
    printf("*zippo = %p, *zippo + 1 = %p\n", *zippo, *zippo + 1);
    //*zippo代表该数组首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。
    //该值的地址是&zippo[0][0],所以* zippo就是& zippo[0][0]。
    printf("zippo[0][0] = %d\n", zippo[0][0]);
    printf("*zippo[0] = %d\n", *zippo[0]);
    printf(" **zippo = %d\n", **zippo);
    //**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。
    printf("zippo[2][1] = %d\n", zippo[2][1]);
    printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo + 2) + 1));
    //相对于zippo[2][1];
    return 0;
}
/*zippo = 0118F780,zippo + 1 = 0118F788
zippo[0] = 0118F780,zippo[0] + 1 = 0118F784
*zippo = 0118F780, *zippo + 1 = 0118F784
zippo[0][0] = 2
*zippo[0] = 2
**zippo = 2
zippo[2][1] = 3
*(*(zippo+2) + 1) = 3*/

在这里插入图片描述

  • 指向多维数组的指针
    如何申明一个指针变量指向一个二维数组:
int (* pz)[2]; // pz指向一个内含两个int类型值的数组
//以上代码把pz声明为指向一个数组的指针,该数组内含两个int类型值。
int * pax[2]; // pax是一个内含两个指针元素的数组,每个元素都指向int的指针
//由于[]优先级高,先与pax结合,所以pax成为一个内含两个元素的数组。
//然后*表示pax数组内含两个指针。最后,int表示pax数组中的指针都指向int类型的值。

声明函数的形参时也可以:

void somefunction( int (* pt)[4] );//这表明pt是一个指向数组(内含4个int类型值)的指针
void somefunction( int pt[][4] );//注意,第1个方括号是空的。空的方括号表明pt是一个指针。
  • 指针的兼容性
    两个类型的指针不能相互幅值
int n = 5;
double x;
int * p1 = &n;
double * pd = &x;
x = n; // 隐式类型转换
pd = p1; // 编译时错误

变长数组

变长数组中的“变”不是指可以修改已创建数组的大小。一旦创建了变长数组,它的大小则保持不变。这里的“变”指的是:在创建数组时,可以使用变量指定数组的维度。

int quarters = 4;
int regions = 5;
double sales[regions][quarters]; // 一个变长数组((variable-length array,VLA)
int sum2d(int rows, int cols, int ar[rows][cols]); // ar是一个变长数组(VLA)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值