c++ primer plus书之--c++函数, 数组参数与指针的关系2

数组怎么在函数的参数中进行传递:

// simple function
#include "iostream"
using namespace std;

const int Size = 8;
// 函数原型, 可以不写变量名, 变量名也可以与函数定义的变量名不同
int sum_arr(int arr[], int n);
// 也可以写成下面这中方式:
// int sum_arr(int* arr, int n);

int main() {
	
	int cookies[Size] = {1, 2, 3, 4, 5,6 ,7, 8};
	int sum = sum_arr(cookies, Size);
	
	cout << "sum = " << sum << endl;
	
	return 0;
}

// 函数定义
int sum_arr(int arr[], int n) 
{
	int total = 0;
	for (int i = 0; i < n; i++)
	{
		total += arr[i];
	}
	return total;
}

程序运行结果为:

对于数组而言:

arr[i] = *(arr + i);

&arr[i] = arr + i;

要记住这两个恒等式, 注意: 将指针(包括数组名)加1, 实际上是加了一个与指针指向的类型的长度相等的值, 对于遍历数组而言, 使用指针加法和数组下标是等效的.

数组作为函数的参数有什么需要注意的

来看个demo 

// simple function
#include "iostream"
using namespace std;

const int Size = 8;
// 函数原型, 可以不写变量名, 变量名也可以与函数定义的变量名不同
int sum_arr(int arr[], int n);
// 也可以写成下面这中方式:
// int sum_arr(int* arr, int n);

int main() {
	
	int cookies[Size] = {1, 2, 3, 4, 5,6 ,7, 8};
	int sum = sum_arr(cookies, Size);
	
	// 数组名就是数组的首地址
	cout << "cookies's address = " << cookies << endl;
	// sizeof跟数组名获取的是数组的总长度byte
	cout << " sizeof cookies = " << sizeof cookies << endl;
	
	sum = sum_arr(cookies, Size);
	cout << "Total cookies eaten : " << sum << endl;
	sum = sum_arr(cookies, 3);
	cout << "First three eaters ate : " << sum << " cookies" << endl;
	// 相当于使用cookies[4]
	sum = sum_arr(cookies + 4, 4);
	cout << "Last four eaters ate : " << sum << " cookies" << endl;
	return 0;
}

// 函数定义
int sum_arr(int arr[], int n) 
{
	int total = 0;
	// 打印的是数组arr的首地址
	cout << "arr = " << arr;
	// 打印的是指针arr的大小
	cout << " sizeof arr = " << sizeof arr << endl;
	for (int i = 0; i < n; i++)
	{
		total += arr[i];
	}
	return total;
}

程序运行结果为:

数组作为参数传递意味着什么:

在上面这个demo中函数调用sum_arr(cookies, Size);将数组第一个元素地址和数组中的元素数目传递给sum_arr函数, sum_arr函数将数组cookies的地址赋给了指针变量arr, 将Size赋给了int变量n, 因此sizeof cookies是cookies的数组长度, 而sizeof arr则是指针变量的长度. 这也意味着程序实际并没有将数组内容传递给函数, 而是将数组的位置, 包含的元素种类及元素数目传递给了函数.

注意:为将数组类型和元素数量告诉数组处理函数, 请通过两个不同的参数来传递它们:

void fillArray(int arr[], int size);

而不要试图使用方括号表示法来传递数组长度

void fillArray(int arr[Size]);

const保护数组

void show_array(const double arr[], int n);

如果数组前面有const修饰, 那么表明在这个函数内, 指针arr指向的是常量数据, 这意味着不能使用arr修改该数据, 也就是可以使用arr[0]这样的值, 但是不能修改.

C++将声明const double arr[]解释为const double* arr, 说明arr指向的是一个常量值.

来看一个数组作为参数传递进函数的完整的例子:

// 数组作为变量传递进函数的demo
// 解释了为什么在函数内部打印通过参数传递进来的数组的sizeof打印的不是数组长度
#include "iostream"

const int Max = 5;
using namespace std;

// 函数原型
int fill_array(double arr[], int limit);
// 这里用const修饰, 是因为这个函数不会修改数组的内容
// 因此为了防止误修改, 用const修饰, 这样函数内部就不允许对数组进行修改
void show_array(const double arr[], int n);
void revalue(double r, double arr[], int n);

int main()
{
	// 声明一个长度为5的数组
	double properties[Max];
	
	// 将数组名作为变量传递给函数, 实际传递进去的
	// 数组的地址, 因此fill_array里如果打印 sizeof properties会打印
	// 指针变量的长度, 而不是数组的长度, 因此一定要把数组的长度通过
	// 其他参数传递进去
	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 << "please input a num : ";			
		}
		revalue(factor, properties, size);
		show_array(properties, size);
	}
	cout << "End." << endl;
	return 0;
}


int fill_array(double arr[], int limit)
{
	double temp;
	int i;
	for (i = 0; i < limit; i++)
	{
		cout << "input value #" << i << " : " << endl;
		cin >> temp;
		if (!cin)
		{
			// 先清空
			cin.clear();
			// 后舍弃输入
			while(cin.get() != '\n')
				continue;
			cout << "input thread terminated: ";	
			break;
		} else if(temp < 0){
			break;
		}
		arr[i] = temp;
	}
	return i;
}


void show_array(const double arr[], int n) 
{
	for (int i = 0; i < n; i++) 
	{
		cout << "Property #" << i << " = " << arr[i] << endl;
	}
}

void revalue(double r, double arr[], int n)
{
	for (int i = 0; i < n; i++)
	{
		arr[i] *= r;
	}
}

程序运行结果为:

怎样使用数组的某个区间

// 使用数组区间的函数
#include "iostream"
using namespace std;

const int Size = 8;

// 函数原型, 参数为数组的开始/结束地址
int sum_arr(int* begin, int* end);

int main() {
	
	int cookies[Size] = {1, 2, 3, 4, 5,6 ,7, 8};
	int sum = sum_arr(cookies, cookies + Size);
	
	// 数组名就是数组的首地址
	cout << "cookies's address = " << cookies << endl;
	// sizeof跟数组名获取的是数组的总长度byte
	cout << " sizeof cookies = " << sizeof cookies << endl;
	
	sum = sum_arr(cookies, cookies + Size);
	cout << "Total cookies eaten : " << sum << endl;
	// 传递进去数组的首地址和第四个元素的首地址, 也就是计算前三位的和
	sum = sum_arr(cookies, cookies + 3);
	cout << "First three eaters ate : " << sum << " cookies" << endl;
	// 相当于使用cookies[4]
	// 
	sum = sum_arr(cookies + 4, cookies + 8);
	cout << "Last four eaters ate : " << sum << " cookies" << endl;
	return 0;
}

// 函数定义
int sum_arr(int* begin, int* end) 
{
	int total = 0;
	int* pt;
	// 由于pt指向的是一个int数组, 里面的变量是int值
	// 因此pt++, 就意味着每次加上int值所占的字节数
	// 也就相当于pt指向下一个元素的位置
	for (pt = begin; pt != end; pt++)
	{
		total += *pt;
	}
	return total;
}

程序运行结果为:

指针和const关系

先看个例子:

int age = 39;

const int* pt = &age;

这里pt指向了一个const int型对象, 因此不能通过pt修改age的数值也就是:

*pt += 1;

这种企图去改变值得错误是不被允许的.

但是age本身不是const的, 因此可以通过age对值进行修改.

再看下面两种情况:

const double price = 1.23;

// 合法的, 因为两个都是const型变量

const double* p_price = &price;

const double price = 1.23;

// 不合法的, 因为指针p_price是非const的, 所以可以通过p_price对price进行修改

double * p_price = &price;

 总结一下就是: c++禁止将const的地址赋值给非const指针.

如果将指针指向指针, 情况会更复杂一点:

1.涉及的是一级间接关系, 则将非const指针赋值给const指针是可以的:

int age = 20;

// 可以通过指针pt对age变量的值进行修改

int* pt = &age;

// 合法但是不能通过pt_const对age值进行修改

const int* pt_const = pt;

2.进入两级间接关系时(注意看这个例子是不对的, 只是用来说明问题):

const int** pp2;

int* p1;

const int n = 33;

// 不允许这样做, 但是我们先假设可以这么做

pp2 = &p1;

// 允许这么做, 因为两边都是const类型的

*pp2 = &n;

// 允许这么做, 因为p1不是const, 但是会改变const n

// 这会出问题, 因此当且仅当只有一层间接关系(如指针指向基本数据类型)时, 才可以将非const地址或指针赋值给const指针.

*p1 = 10;

注意:如果数据本身不是指针, 则可以将const数据或非const数据赋值给const指针, 但只能将非const数据的地址赋值给非const指针.

const数组

假设有一个const数组

const int num[3] = {1, 2, 3};

则禁止将常量数组的地址赋值给非常量指针, 也意味着不能将数组名作为参数传递给使用非常量形参的函数:

int sum(int arr[], int n);

// 非法的, 形参不是const类型的

int j = sum(num, 3);

将指针参数声明为指向常量数据的指针有两个优点:

1.可以避免由于无意间修改数据导致的问题

2.使用const使得函数能够处理const和非const实参, 否则只能处理非const数据

如果条件允许, 则尽量将指针形参声明为const类型的指针.

关于指针和const还有一个细节需要注意:

举个简单的例子:

int age = 20;

const int* pt = &age;

第二行代码, 只能防止通过pt去修改pt所指向的地址里的值, 但是我们却可以将pt指向一个新的地址, 例如:

int num = 1;

pt = &num;

这时候pt指向的地址中的值是1, 但是我们还是不能使用pt来修改它指向的值(1)

还有一种方式使得无法修改指针的值

int time = 6;

// 一个指向const int的指针

const int* pt = &time;

// 一个int型的const 指针

int* const pc = &time;

第三行代码规定了pc只能指向time, 但是允许通过pc来修改time的值, 第二个声明不允许使用pt来修改time的值, 但允许将pt指向新的地址.

也就是pc和*pt是const的, 而pt和*pc是非const的.

我们可以这么记:

const 后面紧跟着的是int则表示不能通过该指针去修改指针指向的地址里的值, 但是可以修改指针指向的地址, 也就是值不能修改, 但可以改地址

const 后面紧跟着的是pointer则表示我们不能修改指针所指向的地址, 但是可以修改指针指向地址里的值, 也就是指针不能修改, 但可以改值.

指针和二维数组:

举一个例子:
 

int data[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int total = sum(data, 3);


那么函数sum的原型该怎么声明呢?
这里data是个数组名, 它有三个元素, 第一个元素本身是个数组, 由4个int值组成. 因此data的类型是指向由4个int组成的数组的指针:

 

int sum(int (*arr)[4], int n);


注意其中的小括号是必不可少的, 如果写成
 

int sum(int *arr[4], int n);


上面这行代码表示 声明一个由4个指向int的指针组成的数组int *(arr[4]), 而不是由4个int组成的数组的指针.
还有一种正确的写法:

 

int sum(int arr[][4], int size);


这种写法更容易理解一些.

下面是完整的函数定义:
 

int sum(int arr[][4], int size)
{
    int total = 0;
    for(int i = 0; i < size; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            total += arr[i][j];
        }
    }
    return total;
}


例子中没有使用const, 是因为arr是指向指针的指针
 

猜你喜欢

转载自blog.csdn.net/c1392851600/article/details/84557792