假设您要编写一个对数组进行操做的函数,目的是要此函数返回数组内全部元素的和。假设marbles为这个int数组的名称。应该如何来调用这个函数?程序员
一种合乎情理的猜想以下:数组
totao = sum(marbles); // 可能的函数调用函数
那么原型应该是什么?数组名同时表明数组首元素的地址,所以实际参数marbles是一个int的地址,应该把它赋给一个类型为指向int的指针的形式参量:优化
int sum(int *ar); //相就的原型指针
函数sum()从该参数能够获得什么信息呢?它获得数组首元素的地址,并且知道能够今后地址找到一个int。请注意它无从知道数组中元素的数量。因而在函数定义中有两种选择,第一种是在函数代码中写上固定的大小。以下所示:code
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; }
上面的代码利用了这样的事实:正如能够在指针符号中使用数组名称同样,也能够数组符号中使用指针。索引
这种函数定义是有限制的,它仅在数组大小为10时能够工做。更灵活的方法是把数组大小作为第二个参数传递给函数。原型
int sum(int *ar,int n) //更通用的方法 { int i; int total = 0; for (i=0;i<n;i++) //使用n表示元素的个数 total+=ar[i]; //ar[i]与*(ar+i)相同 return total; }
此外,关于函数参量还有一件须要说明的事情:在函数原型和函数定义的场合中(而且只有在这两种场合中),可使用int *ar代替int ar[]:编译器
int sum(int ar[],int n);io
不管在任何状况下,形式int *ar 都表示ar是指向Int的指针。形式int ar[]也能够表示ar是指向int的指针,但只是在声明形式参量时才能够这样使用。使用第二种形式能够提醒读者ar不只指向一个int数值,并且它指向的这个int是一个数组中的元素。
程序清单10.10 sum_arr1.c程序
//sum_arr1.c --对一个数组的全部元素求和 //若是不能使用%zd,请使用%u或%lu #include <stdio.h> #define SIZE 10 int sum(int ar[],int n); int main(void) { int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20}; long answer; answer = sum(marbles,SIZE); printf("The total number of marbles is %ld.\n",answer); printf("The size of marbles is %lu bytes.\n",sizeof marbles); return 0; } int sum(int ar[],int n) { int i; int total=0; for(i=0;i<n;i++) total+=ar[i]; printf("The size of ar is %lu bytes.\n",sizeof ar); return total; }
输出结果以下:
The size of ar is 4 bytes. The total number of marbles is 190. The size of marbles is 40 bytes.
请注意marbles的大小为40字节。的确如此,由于marbles包含10个int 类型的数,每一个数占4个字节,所以总共40个字节。可是ar只有4个字节。这是由于ar自己并非一个数组,它是一个指向marbles的首元素的指针 。对于采用4字节地址的计算机系统,指针的大小为4字节(其余系统中地址的大小可能不是4个字节)。总之,在程序清单10.10中,marbles是一个数组,而ar为一个指向marbles首元素的指针,C中数组和指针之间的关系容许您在数组符号中使用指针ar。
10.4.1 使用指针参数
使用数组的函数须要知道什么时候开始和什么时候结束数组。函数sum()使用一个指针参量来肯定数组的开始点,使用一个整数参量来指明数组的元素个数(指针参量同时肯定了数组中数据的类型)。可是这并非向函数传递数组信息的唯一方法。可是这并非向函数传递数组信息的唯一方法。另外一种方法是传递两个指针 ,第一个指针指明数组的起始地址(同前面的的方法相同),第二个指针指明数组的结束地址。程序清单10.11中的示例示意了这种方法。这个例子利用了指针参数是变量这一事实,也就是说,程序中没有使用索引来指示数组中的每一个元素,而是直接修改指针自己,使指针 依次指向各个数组元素。
程序清单10.11 sum_arr2.c程序
//sum_arr1.c --对一个数组的全部元素求和 //若是不能使用%zd,请使用%u或%lu #include <stdio.h> #define SIZE 10 int sump(int * start,int * end); int main(void) { int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20}; long answer; answer = sump(marbles,marbles+SIZE); printf("The total number of marbles is %ld.\n",answer); return 0; } /*使用指针算术*/ int sump(int * start,int * end) { int total=0; while(start < end) { total+=*start; /*把值累加到total上*/ start++; /*把指针向前推动到下一个元素*/ } return total; }
因为指针start最初指向marbles的首元素,所以执行赋值表达式total+=*start时,把首元素的值 加到total上。而后,表达式start++使指针变量start增1,从而指向数组的下一个元素。start是指向int的指针,所以当start增1时它将增长1个int的大小。
请注意函数sump()和sum()结束加法循环的方式不同。函数sum()使用数组元素的个数作为第二个参数,循环利用这个值来控制循环次数:
for(i=0;i<n;i++)
而函数sump()则使用第二个指针来控制循环次数:
while(start < end)
由于这是一个不相等关系的判断,因此处理的最后一个元素将是end所指向的位置以前的元素。这就意味着end实际指向的位置是在数组最后一个元素以后。C保证在为数组分配存储空间的时候,指向数组以后 的第一个位置的指针也是合法的。这使上面例子中采用的结构是有效的,由于start在循环中最后获得的数值是end。请注意使用这种“越界”指针可以使函数调用的形式更加整洁:
sump(marbles,marbles+SIZE);
因为索引是从0开始的,所以marbles+SIZES指向数组结尾处以后的下一个元素。
顺便说一句,尽管C保证指针marbles+SIZES是合法的,但对marbles[SIZE](即该地址存储的内容)不做任何保证。
能够把上面的循环体精简为一行:
total+=*start++ ;
一元运算符*和++具备相等的优先级,但它在结合时是从右向左进行的。这就意味着++应用于start,而不是应用于*start。也就是说是指针自增1,而不是指针所指向的数据自增1。后缀形式start++而不是++start表示先把指针指向的数据加到total上,而后指针再自增1。若是程序使用++start,则顺序就变为指针先自增1,而后再使用其指向的值。
尽管*start++比较经常使用,但为了清晰起见,应该使用*(start++)。程序清单10.12中的程序示意了这些在关优先级的微秒之处:
程序清单10.12
/*order.c --指针运算的优先级*/ #include <stdio.h> int data[2]={100,200}; int moredata[2]={300,400}; int main(void) { int *p1,*p2,*p3; p1=p2=data; p3=moredata; printf(" *p1=%d, *p2=%d, *p3=%d\n",*p1,*p2,*p3); printf("*p1++ =%d, *++p2 =%d, (*p3)++ =%d\n",*p1++,*++p2,(*p3)++); printf(" *p1 =%d,*p2 =%d,*p3 =%d\n",*p1,*p2,*p3); return 0; }
输出结果以下:
*p1=100, *p2=100, *p3=300 *p1++=100,*++p2=200,(*p3)++=300 *p1=200,*p2=200,*p3=301
上面的例子中,只有(*p3)++改变了数组元素的值。其余两个操做增长了指针p1和指针p2,使之指向下一个元素。
10.4.2 评论:指针和数组
从前面的介绍中能够看出,处理数组的函数其实是使用指针作为参数的。可是在编写处理数组的函数时,数组符号和指针符号都是能够选用的。若是使用数组符号,则函数处理数组这一事实更加明显。也有一些程序员可能更习惯于使用指针,以为指针使用起来更加天然。
在C中,两个表达式ar[i]和*(ar+i)的意义是等价的。并且无论ar是一个数组名仍是一个指针变量,这两个表达式均可以正常工做。然而,只有当ar是一个指针变量时,才可使用ar++这样的表达式。
指针符号(尤为在对其使用增量运算符时)更接近于机器语言,并且某些编译器在编译时可以生成效率更高的代码。然而,不少程序员认为程序员的主要任务是保证程序的正确性和易读性,代码的优化应该交给编译器去作。