今天编程序时发生了个这样的错误:
在头文件里 定义了一个数组:
1 char s[]="1234567890";
又定义了一个现显示组的函数:
1 void Display(char* c);
通过下面这两条语句分别在现实函数和主函数中现实数组的大小:
1 sizeof(c); 2 sizeof(s);
现实结果却大相径庭,在主函数中为11,在现实函数中却为4。
经过思考发现,在主函数中s代表的是一个数组,而在现实函数中c代表的是一个指向数组头的指针。数组的大小为11,指针的大小为4。
主程序如下:
1 #include<iostream> 2 #include<stdlib.h> 3 using namespace std; 4 5 char s[]="1234567890"; 6 void Display(char* c); 7 8 void main() 9 { 10 char a; 11 cout<<"这个是在主函数中对数组长度的测试:"<<sizeof(s)<<endl; 12 Display(s); 13 cin>>a; 14 } 15 16 17 void Display(char* c) 18 { 19 cout<<"这个是在子函数中对数组长度的测试:"<<sizeof(c)<<endl; 20 }
现实结果:
我的问题是怎样可以在主函数和子函数中都指的是数组而不是一个指向数组头的指针???
在网上查了几种将数组作为函数参数进行传递的方法,列举如下:
1 #include<iostream> 2 #include<stdlib.h> 3 #include<vector> 4 using namespace std; 5 6 char s[]="1234567890"; 7 int a[10]={0,1}; 8 int b[10]={0,1}; 9 void Display(char* c); 10 void PutArray1(int *p,int length1); 11 void PutArray2(int p[],int length1); 12 void PutArray3(int p[10]); 13 void PutArray4(int (&p)[10]); 14 void PutArray5(vector<int>verc); 15 16 void main() 17 { 18 char q; 19 cout<<"这个是在主函数中对数组长度的测试:"<<sizeof(s)<<endl; 20 Display(s); 21 cout<<"*********************************************"<<endl; 22 PutArray1(a,10); 23 PutArray2(a,10); 24 PutArray3(a); 25 PutArray4(b); 26 cin>>q; 27 } 28 29 30 void Display(char* c) 31 { 32 cout<<"这个是在子函数中对数组长度的测试:"<<sizeof(c)<<endl; 33 } 34 void PutArray1(int *p,int length1) 35 { 36 int length2=sizeof(p); 37 cout<<"第一种方法的输出:"<<endl; 38 cout<<"第一种方法数组的长度为:"<<length2<<endl; 39 for(int i=0;i<length1;i++) 40 { 41 cout<<p[i]; 42 } 43 cout<<endl; 44 } 45 void PutArray2(int p[],int length1) 46 { 47 int length2=sizeof(p); 48 cout<<"第二种方法的输出:"<<endl; 49 cout<<"第二种方法数组的长度为:"<<length2<<endl; 50 for(int i=0;i<length1;i++) 51 { 52 cout<<p[i]; 53 } 54 cout<<endl; 55 } 56 void PutArray3(int p[10]) 57 { 58 int length2=sizeof(p); 59 cout<<"第三种方法的输出:"<<endl; 60 cout<<"第三种方法数组的长度为:"<<length2<<endl; 61 for(int i=0;i<9;i++) 62 { 63 cout<<p[i]; 64 } 65 cout<<endl; 66 } 67 void PutArray4(int (&p)[10]) 68 { 69 int length2=sizeof(p); 70 cout<<"第四种方法的输出:"<<endl; 71 cout<<"第四种方法数组的长度为:"<<length2<<endl; 72 for(int i=0;i<9;i++) 73 { 74 cout<<p[i]; 75 } 76 cout<<endl; 77 } 78 void PutArray5(vector<int>verc) 79 { 80 vector<int>::iterator begin_iter=verc.begin(); 81 vector<int>::iterator end_iter=verc.end(); 82 int size=verc.size(); 83 cout<<"第五种方法的输出:"<<endl; 84 cout<<"第五种方法数组的长度为:"<<size<<endl; 85 cout<<"下面这种方法是采用向量遍历的方法遍历数组:"<<endl; 86 for(vector<int>::iterator iter=begin_iter;iter!=end_iter;iter++) 87 { 88 cout<<*iter; 89 } 90 cout<<endl; 91 cout<<"下面这种方法是采用普通遍历数组的方法遍历数组:"<<endl; 92 for(int i=0;i<size-1;i++) 93 { 94 cout<<verc[i]; 95 } 96 cout<<endl; 97 }
在这里,int *arr和int arr[]的含义相同,编译器自动将 int arr[]替换为int *arr,所以这也解释了上面在主函数和子函数中利用数组名求数组长度会得到不同结果的原因。这种情况只有在数组作为函数参数进行传递时才会发生(C++ Primer Plus,P192)。
其中第四种方法没有理解,疑问暂时留在这里吧。
另外虽然上面四种方法都可以正确地在子函数中传递数组作为参数,但是仍然不能满足博客刚开始的要求:在子函数中可以测的参数数组的长度。后来查看C++ Primer Plus发现书上已经明确指出没有实现这种想法的方法,数组的长度必须在函数中作为参数进行传递。
另外由于第五种方法需要重新定义向量模版,和主题不符,所以在主函数里并没有调用它。例程中给的程序如下所示:
1 vector<int> verc1(a,a+10); 2 vector<int> verc2(b,b+8); 3 PutArray5(verc1); 4 PutArray5(verc2);
上面这五种调用数组的方法只是在传递数组的方式上不同,可以归纳为传递数组的一种方法,即:传递指向数组头的指针和数组的长度。另外一种传递数组的方法是将指向数组头的指针和指向数组尾的指针作为两个参数进行传递,函数定义如下:
1 int sum_arr(const int* begin,const int* end);
用数组名做函数参数与用数组元素作实参有几点不同:
(1)用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时,则要求形参和相应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参两者类型不一致时,机会发生错误。
(2)用普通变量或下标变量作函数参数时,形参变量和实参变量都是由编译系统分配的两个不同的内存单元。在函数调用时进行的值传递是把实参变量的值赋予形参变量。在用数组名做函数参数时,不是进行值的传递,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传递是如何实现的?因为数组名就是数组的首地址。因此用数组名做函数参数时所进行的传递实际上是地址的传递,也就是把实参数组的首地址赋予形参数组名。形参数组名取得该首地址后,也就等于有了具体的地址。实际上是形参数组和实参数组为同一数组,共同使用一段内存空间。
起始地址 2000
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 |
b[0] | b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
设a为实参数组,类型为整形。a占有以2000为首地址的一块内存区。b为形参数组。当进行函数调用时,进行地址传递,把实参数组a的首地址传送给形参数组名b,于是b也取得了该地址2000.至此a、b两数组共同占用以2000为首地址的一段连续内存单元。同时,a和b中下表相同的元素实际上也占相同的内存单元(整形数组每个元素占两个字节)。
例1:有一个一维数组score,存放10个学生的成绩,求平均值。
#include<stdio.h>
#include<string.h>
#include<conio.h>
#include<stdlib.h>
float average(float array[10]){
int i;
float aver,sum=array[0];
for(i=1;i<10;i++)
sum=sum+array[i];
aver=sum/10;
return aver;
}
main(){
float score[10],aver;
int i;
printf("input 10 score:/n");
for(i=0;i<10;i++)
scanf("%f",&score[i]);
printf("/n");
aver=average(score);
printf("average score is %5.2f/n",aver);
}
说明:
(1)用数组名作函数参数,应该在主调函数和被调函数中分别定义数组。
(2)实参数组与形参数组类型应一致,如不一致,结果将出错。
(3)实际上,指定被调函数中形参数组的大小是不起任何作用的,因为C编译器对形参数组大小不做检查,只是将形参数组的首地址传给形参数组。
(4)形参数组也可以不指定大小,定义数组时在数组名后跟一个空的中括号,为了在被调函数中处理数组元素的需要,可以另设一个参数,传递数组元素的个数。
例2:用参数传递数组元素的个数。
#include<stdio.h>
#include<string.h>
#include<conio.h>
#include<stdlib.h>
float average(float array[],int n){
int i;
float aver,sum=array[0];
for(i=1;i<n;i++)
sum=sum+array[i];
aver=sum/n;
return aver;
}
main(){
float score1[5]={98.5,97,91.5,60,55};
float score2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5};
printf("the average of class A is %6.2f/n",average(score1,5));
printf("the average of class B is %6.2f/n",average(score2,10));
}