函数和数组
用一个函数计算数组元素的和,函数需要知道对哪个数组进行累计,因此需要将数组名作为参数传递给它,为使函数通用,而不限于特定长度的数组,还需要传递数组长度。则函数头如下:
int sum_arr(int arr[], int n) //arr=数组名,n=数组长度
方括号指出arr是一个数组,实际上arr是一个指针,但在编写函数的其余部分时,可以将arr看作是数组。
程序7.5
#include<iostream>
const int ArSize = 8;
int sum_arr(int arr[], int n); //函数原型
int main()
{
using namespace std;
int cookies[ArSize] = { 1,2,4,8,16,32,64,128 };
int sum = sum_arr(cookies, ArSize);
cout << "Total cookies eaten: " << sum << "\n";
system("pause");
return 0;
}
int sum_arr(int arr[], int n)
{
int total = 0;
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
使用指针来处理数组
在大多数情况下,C++和C一样将数组名视为指针,C++将数组名解释为第一个元素的地址,但存在一些例外:
- 数组声明使用数组名来标记存储位置
- 对数组名石永sizeof将得到整个数组的长度(字节)
- 将地址运算符&用于数组时,将返回整个数组的地址
程序7.5中的函数调用为:
int sum = sum_arr(cookies, ArSize);
其中cookies为数组名,即第一个元素的地址,因此函数传递的是地址,由于数组元素的类型为int,因此cookies的类型必须是int指针,即int *,则正确的函数头应为:
int sum = sum_arr(int * arr, int n);
这两个函数头都正确,在C++中,当且仅当用于函数头或函数原型中时,int *arr和int arr[]的含义才是相同的,它们都意味着arr是一个int指针。
arr[i] == *(arr + i)
&arr[i] == arr + i
函数调用将数组第一个元素的地址和数组元素的数目传递给函数,程序并没有将数组内容传递给函数,而是将数组的位置(地址)、包含的元素种类(类型)以及元素数目(n)传递给函数。
传递常规变量时,函数使用该变量的拷贝;但传递数组时,函数使用的是原来的数组。这样可以节省复制整个数组所需的时间和内存。
但是,使用原始数据增加了破坏数据的风险,const限定符提供了解决这种问题的办法。
程序7.6
#include<iostream>
const int ArSize = 8;
int sum_arr(int arr[], int n);
int main()
{
int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };
std::cout << cookies << " = array address, ";
std::cout << sizeof cookies << " = sizeof cookies\n";
int sum = sum_arr(cookies, ArSize);
std::cout << "Total cookies eaten: " << sum << std::endl;
sum = sum_arr(cookies, 3);
std::cout << "First three eaters ate " << sum << " cookies.\n";
sum = sum_arr(cookies + 4, 4);
std::cout << "Last four eaters ate " << sum << " cookies.\n";
system("pause");
return 0;
}
int sum_arr(int arr[], int n)
{
int total = 0;
std::cout << arr << " = arr, ";
std::cout << sizeof arr << " = sizeof arr\n";
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
程序7.6说明cookies和arr指向同一个地址,但sizeof cookies的值为32,而sizeof arr的值为4,这是因为sizeof cookies是整个数组的长度,而sizeof arr只是指针变量的长度。
由于sum_arr()只能通过第二个参数获得数组中的元素数量,因此可以“欺骗”函数,如sum = sum_arr(cookies,3);
通过告诉函数cookies有3个元素,让函数计算前三个元素的和。还可以提供假的数组起始位置,sum = sum_arr(cookies + 4, 4);
,计算第5,6,7,8个元素的总和。
填充数组
接受数组名参数的函数访问的是原始数组,因此可以通过调用该函数将值赋给数组元素。
int fill_array(double ar[], int limit)
{
using namespace std;
double temp;
int i;
for (i = 0; i < limit; i++)
{
cout << "Enter value #" << (i+1) << ": ";
cin >> temp;
if (!cin) //错误的输入
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input; input process terminated.\n";
break;
}
else if (temp < 0)
break;
ar[i] = temp;
}
return i;
}
显示数组及用const保护数组
显示数组只需将数组名和填充的元素数目传递给函数,然后该函数使用循环来显示每个元素。在这一过程中要确保显示函数不修改原始数组。
使用普通参数时,函数使用的是数据的副本,然而接受数组名的函数使用的是原始数据,为防止函数无意中修改数组的内容,可在声明形参时使用const:
void show_array(const double ar[], int n);
显示数组:
void show_array(const double ar[], int n)
{
using namespace std;
for (int i = 0; i < n; i++)
{
cout << "Property #" << (i + 1) << ": $";
cout << ar[i] << endl;
}
}
修改数组
void revalue(double r, double ar[], int n)
{
for (int i = 0; i < n; i++)
ar[i] *= r;
}
程序7.7
#include<iostream>
const int Max = 5;
int fill_array(double ar[], int limit);
void show_array(const double ar[], int n);
void revalue(double r, double ar[], int n);
int main()
{
using namespace std;
double properties[Max];
int size = fill_array(properties, Max);
show_array(properties, size);
if (size > 0)
{
cout << "Enter revaluation factor: ";
double factor;
while (!(cin >> factor))
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input; Please enter a number: ";
}
revalue(factor, properties, size);
show_array(properties, size);
}
cout << "Done.\n";
system("pause");
return 0;
}
int fill_array(double ar[], int limit)
{
using namespace std;
double temp;
int i;
for (i = 0; i < limit; i++)
{
cout << "Enter value #" << (i + 1) << ": ";
cin >> temp;
if (!cin) //错误的输入
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input; input process terminated.\n";
break;
}
else if (temp < 0)
break;
ar[i] = temp;
}
return i;
}
void show_array(const double ar[], int n)
{
using namespace std;
for (int i = 0; i < n; i++)
{
cout << "Property #" << (i + 1) << ": $";
cout << ar[i] << endl;
}
}
void revalue(double r, double ar[], int n)
{
for (int i = 0; i < n; i++)
ar[i] *= r;
}
使用数组区间的函数
还有另一种给函数提供所需信息的方法,即指定元素区间(range),可以通过传递两个指针来完成:一个指针标识数组的开头,另一个指针标识数组的尾部。
程序7.8
#include<iostream>
const int ArSize = 8;
int sum_arr(const int * begin, const int * end);
int main()
{
using namespace std;
int cookies[ArSize] = { 1,2,4,8,16,32,64,128 };
int sum = sum_arr(cookies, cookies + ArSize);
cout << "Total cookies eaten: " << sum << endl;
sum = sum_arr(cookies, cookies + 3);
cout << "First three eaters ate " << sum << " cookies.\n";
sum = sum_arr(cookies + 4, cookies + 8);
cout << "Last four eaters ate " << sum << " cookies.\n";
system("pause");
return 0;
}
int sum_arr(const int * begin, const int * end)
{
const int * pt;
int total = 0;
for (pt = begin; pt != end; pt++)
total = total + *pt;
return total;
}
指针和const
可以用两种不同的方式将const用于指针。第一种是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。
声明一个指向常量的指针pt:
int age = 39;
const int * pt = &age;
该声明指出,pt指向一个const int(39),因此不能使用pt来修改这个值,即*pt的值为const,不能被修改。
第一个微妙之处 pt的声明并不意味着他指向的值实际上就是一个常量,而只是意味着对pt而言,这个值是常量。例如,pt指向age,而age不是const,可以直接通过age变量来修改age的值,但不能使用pt指针来修改它:
*pt = 20; //不合法,pt指向一个const int
age = 20; //合法,age不是const
可以将常规变量的地址赋给常规指针,也可以将常规变量的地址赋给指向const的指针,还可以将const变量的地址赋给指向const的指针,但不能将const变量的地址赋给常规指针。
如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据的地址赋给非const指针。
如果将指针指向指针,则情况会更加复杂。
假设有一个由const数据组成的数组:
const int months[12] = {31,28,31,30,31,30,31,31,30,31,30,31}
则禁止将常量数组的地址赋给非常量指针意味着不能将数组名作为参数传递给使用非常量形参的函数:
int sum(int arr[], int n); //应该是 const int arr[]
int j = sum(months, 12); //不允许
上述函数调用试图将const指针(months)赋给非const指针(arr),编译器将禁止这种函数调用。
尽可能使用const将指针参数声明为指向常量数据的指针有两条理由:
- 这样可以避免由于无疑将修改数据而导致的编程错误;
- 使用const使得函数能够处理const和非const实参,否则将只能接受非const数据。
如果条件允许,则应将指针形参声明为指向const的指针。
第二个微妙之处
int age = 39;
const int * pt = &age;
第二个声明中的const只能防止修改pt指向的值(39),而不能防止修改pt的值,也就是说可以将一个新地址赋给pt:
int sage = 80;
pt = &sage; //可以指向另一个位置
第二种使用const的方式使得无法修改指针的值:
int sloth = 3;
const int * ps = &sloth; //一个指向const int 的指针
//不允许使用ps来修改sloth的值,但允许将ps指向另一个位置
int * const finger = &sloth; //一个指向int的const指针
//finger只能指向sloth,但允许使用finger来修改sloth的值
在上面的例子中,finger和*ps是const,而*finger和ps不是。
还可以声明指向const的const指针:
double trouble = 2.0E30;
const double * const stick = &trouble;