GESP四级 - 第一章 - 第2节 - 形参与实参

发布于:2024-05-24 ⋅ 阅读:(65) ⋅ 点赞:(0)

1. 形参与实参

1.1 什么是形参

形参(Formal Parameter)是在函数定义中声明的变量,用于接收调用函数时传递的实际参数值。形参定义了函数可以接受的数据类型和数量,它们在函数被调用时才会被创建,并在函数执行完毕后被销毁。

形参的声明语法如下:

返回值类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...) {
    // 函数体
}

例如,以下函数有两个形参 ab,它们的类型都是 int

int add(int a, int b) {
    return a + b;
}

在函数定义中,形参的名称可以自由选择,但应该具有描述性,以便于理解其含义。形参的类型可以是任何有效的 C++ 数据类型,包括内置类型(如 intdoublechar 等)和用户自定义类型(如结构体、类等)。

形参在函数内部作为局部变量使用,它们的初始值由调用函数时传递的实际参数确定。在函数执行期间,可以像使用普通变量一样使用形参,对其进行读取、修改等操作。

需要注意的是,形参是函数定义的一部分,它们只在函数内部可见,而不能在函数外部访问。此外,形参是按值传递的,即在函数调用时,实际参数的值被复制给形参,函数内部对形参的修改不会影响实际参数。如果需要在函数内部修改实际参数的值,可以使用引用传递或指针传递。

总之,形参是函数定义中的一种特殊变量,用于接收调用函数时传递的实际参数值,并在函数内部使用。理解形参的概念和使用对于编写和调用函数非常重要。

1.2 什么是实参

实参(Actual Parameter)是在调用函数时传递给函数的实际值或表达式,它们对应于函数定义中的形参。实参提供了函数执行所需的具体数据,它们可以是变量、常量、表达式或其他函数的返回值。

在函数调用时,实参的值会被复制或传递给相应的形参,函数内部通过形参来访问和操作这些值。实参的数量、类型和顺序必须与函数定义中的形参相匹配,否则会导致编译错误或运行时错误。

函数调用时实参的传递语法如下:

函数名(实参1, 实参2, ...);

例如,对于以下函数定义:

int add(int a, int b) {
    return a + b;
}

我们可以使用以下代码调用该函数并传递实参:

int result = add(10, 20);

在这个例子中,1020 是传递给 add 函数的实参,它们分别对应于函数定义中的形参 ab。函数内部通过 ab 来访问实参的值,并执行相应的操作。

实参可以是各种类型的值或表达式,包括:

1 . 字面量:如整数、浮点数、字符等。

int result = add(10, 20);

2 . 变量:传递变量的值给函数。

int x = 10, y = 20;
int result = add(x, y);

3 . 表达式:传递表达式的计算结果给函数。

int result = add(5 + 3, 2 * 4);

4 . 函数调用:传递其他函数的返回值给函数。

int square(int x) {
    return x * x;
}
int result = add(square(3), square(4));

实参是函数调用的一部分,它们提供了函数执行所需的具体数据。通过传递适当的实参,我们可以控制函数的行为和结果。理解实参的概念和使用对于正确调用函数和传递数据非常重要。

1.3 形参与实参的关系

形参和实参是函数定义和函数调用中的两个重要概念,它们之间存在着密切的关系。下面我们来详细探讨形参和实参之间的关系:

1 . 对应关系:

  • 函数调用时,实参的数量、类型和顺序必须与函数定义中的形参相匹配。
  • 每个实参对应一个形参,按照顺序一一对应。
  • 实参的类型必须与对应的形参类型兼容,或者能够隐式转换为形参的类型。

2 . 值传递:

  • 默认情况下,实参是按值传递给形参的。
  • 在函数调用时,实参的值被复制给对应的形参,函数内部操作的是形参的副本。
  • 函数内部对形参的修改不会影响实参的值。

3 . 参数匹配:

  • 实参的类型必须与对应的形参类型相同或兼容。
  • 如果实参的类型与形参的类型不完全匹配,编译器会尝试进行隐式类型转换。
  • 如果无法进行隐式类型转换,或者转换可能导致数据丢失,编译器会报错。

4 . 参数数量:

  • 实参的数量必须与函数定义中的形参数量相同。
  • 如果实参的数量多于或少于形参的数量,编译器会报错。

5 . 参数顺序:

  • 实参的顺序必须与形参的顺序相同。
  • 实参按照从左到右的顺序依次对应形参。

6 . 默认参数:

  • 在 C++中,可以为形参指定默认值。
  • 如果在函数调用时省略了某个实参,对应的形参将使用默认值。
  • 默认参数必须从右向左连续指定,不能跳过中间的参数。

7 . 引用传递和指针传递:

  • 除了按值传递,C++还支持按引用传递和按指针传递。
  • 按引用传递时,形参作为实参的别名,函数内部对形参的修改会影响实参的值。
  • 按指针传递时,形参是指向实参的指针,函数内部通过指针可以修改实参的值。

下面是一个示例,演示了形参和实参的关系:

// 函数定义
int add(int a, int b) {
    return a + b;
}

// 函数调用
int main() {
    int x = 10, y = 20;
    int result = add(x, y);  // 实参 x 和 y 对应形参 a 和 b
    cout << "Result: " << result << endl;
    return 0;
}

在这个例子中,add函数有两个形参ab,在main函数中调用add函数时,实参xy分别对应形参ab,并将它们的值传递给形参。函数内部通过形参ab来访问实参的值,并执行相应的操作。

理解形参和实参的关系对于正确定义和调用函数非常重要。通过匹配实参和形参,我们可以将数据传递给函数,控制函数的行为和结果。同时,也要注意参数的类型、数量和顺序,以及传递方式的选择,以确保函数的正确性和可读性。

1.4 按值传递

按值传递是函数参数传递的默认方式,也是最常见的参数传递方式之一。在按值传递中,实参的值被复制给函数的形参,函数内部操作的是形参的副本,而不是实参本身。

按值传递的特点如下:

  1. 实参的值被复制给形参,函数内部使用的是形参的副本。
  2. 函数内部对形参的修改不会影响实参的值。
  3. 实参和形参是独立的存储空间,它们之间没有直接的关联。

按值传递的语法如下:

返回值类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...) {
    // 函数体
}

下面是一个按值传递的示例:

void swapValues(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    cout << "Inside function: a = " << a << ", b = " << b << endl;
}

int main() {
    int x = 10, y = 20;
    cout << "Before function call: x = " << x << ", y = " << y << endl;
    swapValues(x, y);
    cout << "After function call: x = " << x << ", y = " << y << endl;
    return 0;
}

输出结果:

Before function call: x = 10, y = 20
Inside function: a = 20, b = 10
After function call: x = 10, y = 20

在这个示例中,swapValues函数接受两个整型参数ab,函数内部交换了ab的值。但是,由于是按值传递,函数内部操作的是ab的副本,而不是实参xy。因此,在函数调用后,实参xy的值并没有发生改变。

按值传递的优点是简单明了,函数内部对形参的修改不会影响实参,提供了一定的安全性和隔离性。但是,当传递大型对象或数组时,按值传递会导致性能开销,因为需要复制整个对象或数组。在这种情况下,可以考虑使用引用传递或指针传递来提高效率。

总之,按值传递是函数参数传递的一种基本方式,它将实参的值复制给形参,函数内部操作的是形参的副本。理解按值传递的特点和适用场景,可以帮助我们合理选择参数传递方式,编写正确高效的函数。

1.5 按引用传递

按引用传递是 C++ 中另一种常用的参数传递方式,它允许函数直接访问和修改实参的值。在按引用传递中,形参作为实参的别名(引用),对形参的任何操作都会直接影响实参。

按引用传递的特点如下:

  1. 形参是实参的引用(别名),它们指向同一个内存位置。
  2. 通过形参可以直接访问和修改实参的值。
  3. 函数内部对形参的修改会影响实参的值。
  4. 按引用传递可以避免大型对象或数组的复制,提高性能。

按引用传递的语法如下:

返回值类型 函数名(参数类型1& 参数名1, 参数类型2& 参数名2, ...) {
    // 函数体
}

注意,在形参的类型后面添加了 & 符号,表示该形参是一个引用。

下面是一个按引用传递的示例:

void swapValues(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
    cout << "Inside function: a = " << a << ", b = " << b << endl;
}

int main() {
    int x = 10, y = 20;
    cout << "Before function call: x = " << x << ", y = " << y << endl;
    swapValues(x, y);
    cout << "After function call: x = " << x << ", y = " << y << endl;
    return 0;
}

输出结果:

Before function call: x = 10, y = 20
Inside function: a = 20, b = 10
After function call: x = 20, y = 10

在这个示例中,swapValues 函数的参数 ab 都是引用类型,它们分别绑定到实参 xy。在函数内部,通过修改 ab 的值,实际上就是直接修改 xy 的值。因此,在函数调用后,实参 xy 的值发生了交换。

按引用传递的优点是可以直接修改实参的值,避免了大型对象或数组的复制,提高了性能。但是,由于函数内部可以修改实参的值,使用时需要格外小心,确保引用参数的使用是安全和正确的。

在使用按引用传递时,需要注意以下几点:

  1. 引用参数必须是一个左值(可以取地址的表达式),不能是常量或字面量。
  2. 引用参数的类型必须与实参的类型匹配或兼容。
  3. 在函数内部,不能将引用参数重新绑定到其他对象上。

总之,按引用传递是 C++ 中一种强大的参数传递方式,它允许函数直接访问和修改实参的值,提高了性能和灵活性。合理使用按引用传递可以使代码更加高效和简洁,但同时也需要注意引用参数的正确性和安全性。

好的,下面是5个按引用传递的正确示例:

好的,以下是按照给定格式的5个按引用传递的示例:

示例1: 交换两个整数的值

#include <iostream>
using namespace std;

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;
    cout << "交换前: x = " << x << ", y = " << y << endl;
    swap(x, y);
    cout << "交换后: x = " << x << ", y = " << y << endl;
    return 0;
}

示例2: 修改字符串的内容

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

void toUpperCase(string& str) {
    for (char& c : str) {
        c = toupper(c);
    }
}

int main() {
    string message = "Hello, world!";
    cout << "转换前: " << message << endl;
    toUpperCase(message);
    cout << "转换后: " << message << endl;
    return 0;
}

示例3: 计算数组元素的和

#include <iostream>
using namespace std;

void sumArray(int arr[], int size, int& sum) {
    sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    int sum = 0;
    sumArray(numbers, size, sum);
    cout << "数组元素的和: " << sum << endl;
    return 0;
}

示例4: 修改结构体成员的值

#include <iostream>
#include <string>
using namespace std;

struct Person {
    string name;
    int age;
};

void increaseAge(Person& person) {
    person.age++;
}

int main() {
    Person john = {"John", 30};
    cout << "修改前: " << john.name << " 的年龄是 " << john.age << " 岁." << endl;
    increaseAge(john);
    cout << "修改后: " << john.name << " 的年龄是 " << john.age << " 岁." << endl;
    return 0;
}

示例5: 返回多个值

#include <iostream>
using namespace std;

void getMinMax(int arr[], int size, int& min, int& max) {
    min = max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] < min) {
            min = arr[i];
        }
        if (arr[i] > max) {
            max = arr[i];
        }
    }
}

int main() {
    int numbers[] = {10, 5, 8, 20, 3};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    int min, max;
    getMinMax(numbers, size, min, max);
    cout << "最小值: " << min << endl;
    cout << "最大值: " << max << endl;
    return 0;
}

这些示例按照给定的格式展示了按引用传递在不同场景下的应用。每个示例都包含了完整的代码,并使用中文进行了输出和注释。希望这些示例能够清晰地展示按引用传递的用法和效果。

1.6 数组作为函数参数

在 C++ 中,数组可以作为函数的参数进行传递。当数组作为函数参数时,实际上传递的是数组的首地址,而不是整个数组的副本。这意味着在函数内部对数组元素的修改会直接影响原始数组。

将数组作为函数参数传递的语法如下:

返回值类型 函数名(数组类型 数组名[],int 数组大小)
{
    // 函数体
}

其中,数组类型是数组元素的类型,数组名是形参的名称,数组大小是数组的大小(元素个数)。注意,数组作为函数参数时,不需要指定数组的大小,因为数组大小可以通过另一个参数或其他方式传递。

下面是一个将数组作为函数参数的示例:

示例: 计算数组元素的平均值

#include <iostream>
using namespace std;

double calculateAverage(int arr[], int size) {
    double sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return sum / size;
}

int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    double average = calculateAverage(numbers, size);
    cout << "数组元素的平均值: " << average << endl;
    return 0;
}

在这个示例中,calculateAverage函数接受一个整型数组arr和数组的大小size作为参数。函数内部通过循环遍历数组元素,计算元素的总和,然后除以数组的大小,得到平均值并返回。

main函数中,我们定义了一个整型数组numbers,并使用sizeof运算符计算数组的大小。然后,将数组numbers和大小size作为参数传递给calculateAverage函数,得到数组元素的平均值,并输出结果。

需要注意的是,当数组作为函数参数传递时,实际上传递的是数组的首地址。因此,在函数内部对数组元素的修改会直接影响原始数组。这种行为称为"按引用传递"。

另外,在函数参数中,我们也可以使用指针来表示数组,语法如下:

返回值类型 函数名(数组类型* 指针名,int 数组大小)
{
    // 函数体
}

使用指针作为函数参数可以更明确地表示传递的是数组的地址,但功能上与直接使用数组名是等价的。

总之,将数组作为函数参数传递是 C++ 中常见的操作,它允许函数直接访问和修改原始数组的元素。通过将数组的地址传递给函数,可以避免复制整个数组,提高了效率。但同时也需要注意,在函数内部对数组元素的修改会影响原始数组,使用时要谨慎。

示例1: 查找数组中的最大元素

#include <iostream>
using namespace std;

int findMax(int arr[], int size) {
    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

int main() {
    int numbers[] = {10, 5, 8, 20, 3};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    int maxElement = findMax(numbers, size);
    cout << "数组中的最大元素: " << maxElement << endl;
    return 0;
}

在这个示例中,findMax函数接受一个整型数组arr和数组的大小size作为参数。函数内部通过循环遍历数组元素,找到最大的元素并返回。在main函数中,将数组numbers和大小size传递给findMax函数,得到数组中的最大元素并输出。

示例2: 反转数组中的元素

#include <iostream>
using namespace std;

void reverseArray(int arr[], int size) {
    int start = 0;
    int end = size - 1;
    while (start < end) {
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    cout << "反转前: ";
    for (int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;
    reverseArray(numbers, size);
    cout << "反转后: ";
    for (int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;
    return 0;
}

在这个示例中,reverseArray函数接受一个整型数组arr和数组的大小size作为参数。函数内部使用双指针的方法,从数组的两端开始交换元素,直到指针相遇。在main函数中,将数组numbers和大小size传递给reverseArray函数,实现数组元素的反转,并输出反转前后的数组。

示例3: 计算数组中正数的个数

#include <iostream>
using namespace std;

int countPositives(int arr[], int size) {
    int count = 0;
    for (int i = 0; i < size; i++) {
        if (arr[i] > 0) {
            count++;
        }
    }
    return count;
}

int main() {
    int numbers[] = {-2, 5, 0, -8, 10, 3};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    int positiveCount = countPositives(numbers, size);
    cout << "数组中正数的个数: " << positiveCount << endl;
    return 0;
}

在这个示例中,countPositives函数接受一个整型数组arr和数组的大小size作为参数。函数内部通过循环遍历数组元素,统计正数的个数并返回。在main函数中,将数组numbers和大小size传递给countPositives函数,得到数组中正数的个数并输出。

这些示例展示了将数组作为函数参数的不同应用场景,包括查找最大元素、反转数组和计算正数个数。通过将数组传递给函数,我们可以对数组进行各种操作和计算,使代码更加模块化和可重用。

1.7 指针作为函数参数

在 C++ 中,指针也可以作为函数的参数进行传递。通过将指针传递给函数,我们可以在函数内部直接访问和修改指针所指向的内存位置的值。这提供了一种高效且灵活的方式来操作数据。

将指针作为函数参数传递的语法如下:

返回值类型 函数名(指针类型* 指针名)
{
    // 函数体
}

其中,指针类型是指针所指向的数据类型,指针名是形参的名称。

下面是一个将指针作为函数参数的示例:

示例1: 交换两个整数的值

#include <iostream>
using namespace std;

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    cout << "交换前: x = " << x << ", y = " << y << endl;
    swap(&x, &y);
    cout << "交换后: x = " << x << ", y = " << y << endl;
    return 0;
}

在这个示例中,swap函数接受两个整型指针ab作为参数。函数内部通过解引用操作符*来访问指针所指向的内存位置,并交换它们的值。

main函数中,我们定义了两个整型变量xy,并将它们的地址&x&y作为参数传递给swap函数。在函数内部,通过指针来交换xy的值。最后,输出交换前后的xy的值。

示例2: 修改字符串的内容

#include <iostream>
#include <cstring>
using namespace std;

void toUpperCase(char* str) {
    int length = strlen(str);
    for (int i = 0; i < length; i++) {
        if (str[i] >= 'a' && str[i] <= 'z') {
            str[i] = str[i] - 'a' + 'A';
        }
    }
}

int main() {
    char message[] = "Hello, world!";
    cout << "转换前: " << message << endl;
    toUpperCase(message);
    cout << "转换后: " << message << endl;
    return 0;
}

在这个示例中,toUpperCase函数接受一个字符指针str作为参数。函数内部通过指针来访问字符串的每个字符,并将小写字母转换为大写字母。

main函数中,我们定义了一个字符数组message,并将其作为参数传递给toUpperCase函数。函数内部通过指针来修改字符串的内容,将其转换为大写。最后,输出转换前后的字符串。

示例3: 动态分配内存

#include <iostream>
using namespace std;

void allocateArray(int** arr, int size) {
    *arr = new int[size];
    for (int i = 0; i < size; i++) {
        (*arr)[i] = i + 1;
    }
}

int main() {
    int* numbers = nullptr;
    int size = 5;
    allocateArray(&numbers, size);
    cout << "动态分配的数组: ";
    for (int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;
    delete[] numbers;
    return 0;
}

在这个示例中,allocateArray函数接受一个指向整型指针的指针arr和数组大小size作为参数。函数内部使用new运算符动态分配一个整型数组,并将其地址赋值给指针*arr。然后,通过指针来初始化数组的元素。

main函数中,我们定义了一个整型指针numbers和数组大小size。将numbers的地址&numberssize作为参数传递给allocateArray函数。函数内部动态分配一个数组,并将其地址赋值给numbers。最后,输出动态分配的数组,并使用delete[]运算符释放内存。

这些示例展示了将指针作为函数参数的不同应用场景,包括交换整数值、修改字符串内容和动态分配内存。通过将指针传递给函数,我们可以在函数内部直接访问和修改指针所指向的内存位置的值,提供了一种高效且灵活的方式来操作数据。但同时也需要注意指针的正确使用和内存管理,避免出现内存泄漏或非法访问的问题。


网站公告

今日签到

点亮在社区的每一天
去签到