您的当前位置:首页正文

C语言中参数传递(精)

2021-07-06 来源:一二三四网


二. 参数传递

函数的形参的初始化和变量的初始化一样,如果形参具有非引用类型,则复制实参的值, 如果形参为引用类型,则它是实参的别名。

1. 非引用实参

普通的非引用类型的函数通过复制对应的实参实现初始化。当用实参副本初始化形参时, 函数并没有调用所传递的实参本身,因此不会修改实参的值。

注解:非引用形参表示对应实参的局部副本, 对这类行参的修改仅仅改变了局部副本的值, 一旦函数执行结束,这些局部变量的值也就没有了。

a. 指针形参

指针形参与其他非引用类型的行参一样, 如果将新指针赋给行参, 主调函数使用的实参指 针的值没有改变。事实上被复制的指针只影响对指针的赋值。指针形参是 const 类型还是非 const 类型,将影响函数调用所使用的实参。

b. const行参

在调用函数时,如果该函数使用非引用的非 const 形参,则既给该函数传递 const 实参也 可传递非 const 的实参 (因为改变形参不影响 const 的实参,所以 const 实参不会被改变 。如 果将形参定义为非引用的 const 类型,则在函数中,不可以改变实参的局部副本,由

于实参 是以副本的形式传递,因此传递给函数形参既可是 const 也可是非 const 对象。

注意:尽管函数的形参是 const ,但是编译器却将该行参声明视为普通的 int 型。

void fcn(const int i;

void fcn(int i;

为了兼顾 C 语言,认为这两种定义并不区别。

c. 复制实参的局限性

不适合复制实参的情况包括:

当需要在函数中修改实参的值时

当需要以大型对象作为实参传递时, 对实际的应用而言, 复制对象所付出的时间和存储空 间代价往往很大。

但没有办法实习对象的复制时

对于以上几种情况,有效的办法是将形参定义为引用或指针。

2. 引用实参

与所有引用一样, 引用形参直接关联到其所绑定的对象, 而并非这些对象的副本。 定义引

用时, 必须用与该引用绑定的对象初始化该引用。 引用形参以完全相同的方式工作。 每次调 用函数时,引用形参被创建并与相应的实参关联。

a. 使用引用形参返回额外的信息

函数只能返回单个值, 但有时候函数有不止一个的内容需要返回。 这时候我们可以通过函 数传递一个额外的引用实参,用于返回额外的信息。

b. 利用 const 引用避免复制

对于大型对象复制效率太低了, 有些类型甚至无法复制, 利用 const 引用就可以避免复制, 引用形参是引用,所以不复制实参,又因为形参是 const 引用,所以不能使该引用来修改实 参。

c. 更灵活的指向 const 的引用

如果函数具有普通的非 const 引用形参,则不能通过 const 对象进行调用,因为函数可以 修改传来的参数,但这样就违背了实参的 const 特性。

int incr(int &val

{

return ++val;

}

int main(

{

short v1=0;

const int v2=42;

int v3=incr(v1; //error, v1不是整型

v3=incr(v2; //error, v2使 const 对象

v3=incr(0; //error, 字面值不是左值

v3=incr(v1+v2; //error, 加法不能作为左值

int v4=incr(v3; //ok, v3是一个非 const 的整型值

}

问题的关键是非 const 引用形参只能与完全相同的非 const 对象关联。

最佳实践:应该将不需要修改的引用定义为 const 引用。普通的非 const 引用形参在使用 时不太灵活。这样的形参既不能被 const 对象初始化,也不能用字面值或产生右值的表达式 初始化。

d. 传递指向指针的引用

实现两个指针的交换:

void ptrswap(int* &v1, int* &v2

{

int* temp=v2;

v2=v1;

v1=temp;

}

行参 int* &val的定义从右向左理解:v1是一个引用,与指向 int 型对象的指针相关联。 也就是说, v1只是传递进 ptrswap 函数的任意指针的别名。

3. vector和其他容器类型的行参

最佳实践:通常, 函数不应该有 vector 或其他标准容器库类型的实参。 调用含有普通的非 引用 vector 行参的函数将会复制 vector 的每一个元素。 从避免复制 vector 的角度出发, 应考 虑将形参声明为引用类型。

4. 数组形参

a. 数组形参的定义

数组会被自动转换为指针 , 通常,将数组形参直接定义为指针要比数组语法更好,这样就 明确的表示,函数操纵是指向数组元素的指针,而不是数组本身。

当编译器检查数组形参关联的实参时, 他只会检查实参是不是指针, 指针的类型和数组元 素的类型是否匹配,而不会检查数组的长度。

b. 数组实参

和其他类型一样, 数组形参可定义为引用或非引用类型, 大部分情况下, 数组以普通的非 引用类型传递,此时数组会转换为指针。

最佳实践:当不需要修改数组形参的元素时,函数应该将形参的定义为指向 const 对象的 指针。

c. 通过引用传递数组

与其他类型一样, 数组形参可以声明为数组的引用, 如果形参是数组的引用, 编译器不会 将数组实参转化为指针, 而是传递数组的引用本身, 在这种情况下, 数组的大小成为形参和 实参的一部分。编译器检查数组实参的大小与形参的大小是否匹配。

void print(int (&arr[10] ; //形参是一个数组的引用,数组的大小确定

int main(

{

int i=0,j[2]={0,1};

int k[10]={0,1,2,3,4,5,6,7,8,9};

print(&i; //error,参数不是 10个整型元素的数组

print(j; // error,参数不是 10个整型元素的数组

print(k; //ok, 参数是 10个整型元素的数组。

}

注解:&arr两本的括号是必须的,因为下标操作具有更高的优先级

f(int &arr[10] //error,arrs是一个含有 10个引用的数组

f(int (&arr[10] //ok, arr 是一个引用,他和一个含有 10个元素的数组关联

d. 多维数组的传递

C ++没有多维数组,所谓多维数组实际就是指数组的数组。除了第一维以外的所有维的 长度都是元素类型的一部分。

5. 传递给函数的数组的处理

任何数组的处理程序都要保证程序停留在数组的边界内。

a. 使用标准库规范, 传递指向数组的第一个元素和最后一个元素的下一个位置的指针。 这中 技术风格由标准库的技术启发而得。

b. 显式传递表示数组大小的形参

void print(const int ia[], size_t size;

6. main:处理命令行选项

7.含有可变形参的函数

在无法列举出传递给函数的所有实参的类型和数目时, 可以使用省略符形参。 省略符暂停 了类型检查机制。它们的出现告诉编译器,当调用函数时,可以有 0或多个实参, 而实参的 类型未知。

两种省略形参的形式

void foo(param_list, …;

void foo(…;

因篇幅问题不能全部显示,请点此查看更多更全内容