Bootstrap

C++函数声明与使用:从基础到实战

一、简介

在编程中,函数是组织代码的基本单元。它们允许我们将代码分解成可重用的模块,从而提高代码的可读性、可维护性和可扩展性。本文将深入探讨函数的基础知识、语法、调用方式以及在实际编程中的应用。我们将通过一个具体的C++示例来展示如何编写和使用函数。


二、基础知识

1. 什么是函数?

函数是一段可以重复调用的代码块,它接受输入参数并返回一个结果。函数的主要目的是将复杂的任务分解成更小、更易管理的部分。

示例:一个简单的函数
#include <iostream>
using namespace std;

// 定义一个函数,计算两个整数的和
int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 5); // 调用函数
    cout << "3 + 5 = " << result << endl;
    return 0;
}

输出:

3 + 5 = 8

在这个示例中,add函数接受两个整数参数ab,并返回它们的和。通过调用add(3, 5),我们可以轻松地计算两个数的和。


2. 函数的组成部分

  • 函数声明:指定函数的名称、返回类型和参数列表。
  • 函数定义:包含函数的具体实现。
  • 函数调用:在程序中调用函数以执行其代码。
示例:函数声明与定义
#include <iostream>
using namespace std;

// 函数声明
int multiply(int a, int b);

int main() {
    int result = multiply(4, 6); // 调用函数
    cout << "4 * 6 = " << result << endl;
    return 0;
}

// 函数定义
int multiply(int a, int b) {
    return a * b;
}

输出:

4 * 6 = 24

在这个示例中,multiply函数先声明后定义。函数声明告诉编译器函数的存在,而函数定义则提供了具体的实现。


3. C/C++函数调用的压栈模型

在C/C++中,函数调用是通过栈(Stack)来管理的。栈是一种后进先出(LIFO, Last In First Out)的数据结构,用于存储函数调用的上下文信息。理解函数调用的压栈模型对于掌握程序执行流程、调试代码以及优化性能非常重要。

一个典型的栈帧结构如下:

栈帧内容描述
返回地址函数执行完毕后返回到调用点的地址。
参数传递给函数的参数,按从右到左的顺序压入栈。
局部变量函数内部定义的变量。
寄存器状态保存调用函数前的寄存器值,以便函数返回后恢复。

栈的特点是:

  • 后进先出:最后压入栈的数据最先弹出。
  • 自动管理:栈的分配和释放由编译器自动完成。

当一个函数被调用时,系统会按照以下步骤将数据压入栈中:

1. 压入返回地址

调用函数时,程序需要记住函数执行完毕后返回到哪里。因此,返回地址(即调用函数的下一条指令地址)会被压入栈中。

2. 压入参数

函数的参数按照从右到左的顺序压入栈中。例如,对于函数func(a, b, c),参数c先压入栈,然后是b,最后是a

3. 压入局部变量

函数内部定义的局部变量会被压入栈中。

4. 保存寄存器状态

为了保证函数返回后程序能继续正确执行,调用函数前的寄存器状态会被保存到栈中。
在这里插入图片描述

图片来源


示例分析

以下是一个简单的C++示例,展示函数调用的压栈过程:

#include <iostream>
using namespace std;

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

int main() {
    int x = 3, y = 5;
    int sum = add(x, y);
    cout << "Sum: " << sum << endl;
    return 0;
}

压栈过程详解

  1. 调用add函数前

    • main函数的栈帧中包含局部变量xy
  2. 调用add函数时

    • 返回地址(main函数中add(x, y)的下一条指令地址)被压入栈。
    • 参数yx依次压入栈(从右到左)。
    • add函数的栈帧被分配,局部变量result被压入栈。
  3. add函数执行完毕后

    • 返回值result被存储到寄存器或栈中。
    • add函数的栈帧被释放。
    • 程序跳转到返回地址,继续执行main函数。

4. 函数的优点

  • 代码复用:避免重复代码,提高开发效率。
  • 模块化:将程序分解成多个功能模块,便于维护和调试。
  • 抽象:隐藏实现细节,只暴露必要的接口。

三、示例代码

问题描述

给定一个包含 n 个整数的数组 S,判断是否存在三个元素 a, b, c,使得 a + b + c = 0?找出所有满足条件的三元组,且这些三元组中的元素必须按非降序排列(即 a ≤ b ≤ c),并且解集中不能包含重复的三元组。

示例:

输入:S = [-10, 0, 10, 20, -10, -40]
输出:[[-10, -10, 20], [-10, 0, 10]]

以下是一个C++示例,展示了如何编写和使用函数。该示例实现了一个threeSum函数,用于找到数组中所有满足a + b + c = 0的三元组。

#include <algorithm>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型vector 
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > threeSum(vector<int>& num) {
        // write code here
        int n = num.size();
        int m=0;
        vector<vector<int> > result;
        for(int i=0; i<n; i++){
            for(int j=i+1; j<n; j++){
                int obj = 0 - num[i] -num[j];
                int z = findNumber(num, obj,j);
                if(obj ==4){
                    cout << num[i] << num[j] <<num[z] <<endl;
                }
                
                if(z > 0 ){
                    
                    bool AAA = moto(result,num[i],num[j],num[z]);
                    int opc =num[i];  // 此时已经麻木了
                    int ua =num[j];
                    int can =num[z];
                    sortThreeDescending(opc,ua,can);
                    // cout << num[i] << num[j] <<num[z] <<endl;
                    if(!AAA){
                        
                        int pos = position(result, opc,ua,can);
                        // cout << pos << num[i] << num[j] <<num[z] <<endl;
                        auto p = result.begin() + pos;
                        result.insert(p,{opc,ua,can});
                    }
                    
                }

            }
        }
        return result;
    }

    int findNumber(const std::vector<int>& arr, int target, int j) {
        for (int i = 0; i < arr.size(); i++) {
            if (arr[i] == target && i > j) {
                return i; // 返回目标值的位置
            }
        }
        return -1; // 如果未找到,返回 -1
    }

    bool moto(vector<vector<int> >& result, int i, int j, int z) {
        int m = result.size();
        for (int a = 0; a < m; a++) {
            // 检查当前三元组是否包含i, j, z
            bool contains_i = false, contains_j = false, contains_z = false;
            for (int b = 0; b < 3; b++) {
                if (result[a][b] == i && contains_i == false) {contains_i = true;}
                else if (result[a][b] == j && contains_j == false) {contains_j = true;}
                else if (result[a][b] == z && contains_z == false) {contains_z = true;}
            }
            // 如果三个数都在当前三元组中,返回true
            if (contains_i && contains_j && contains_z) {
                return true;
            }
        }
        // 未找到匹配的三元组,返回false
        return false;
    }

    void sortThreeDescending(int& a, int& b, int& c) {
        if (a > b) std::swap(a, b);
        if (a > c) std::swap(a, c);
        if (b > c) std::swap(b, c);
    }

    int position(vector<vector<int> >& result, int i, int j, int z){
        int m = result.size();
        int pos = 0;
        for (int a = 0; a < m; a++){
            if( i > result[a][0] ){
                pos = a+1;
           }
            else if(i == result[a][0])
            {
                if( j > result[a][1] ){
                    pos = a+1;
                    
                }
                else if( j  == result[a][1]){
                    if(z > result[a][2]){ pos = a+1;  }
                    else{ return a;  }
                }
                else{
                    return a;
                }   
            }
            else{
                return pos;
            }
            
        }

        return pos;

        if(m ==0){
            return 0;
        }
        
    }
};

四、函数功能解析

1. threeSum函数

threeSum函数接受一个整数向量num,并返回所有满足a + b + c = 0的三元组。该函数通过嵌套循环遍历数组,并使用findNumber函数查找满足条件的第三个元素。

2. findNumber函数

findNumber函数在数组中查找特定值,并返回其索引。如果未找到,则返回-1。

3. moto函数

moto函数检查当前三元组是否已存在于结果中,以避免重复。

4. sortThreeDescending函数

sortThreeDescending函数对三个整数进行降序排序,确保三元组的顺序一致。

5. position函数

position函数确定新三元组在结果中的插入位置,以保持结果的有序性。


五、语法总结

1. 函数的基本语法

在C++中,函数的基本语法如下:

返回类型 函数名(参数列表) {
    // 函数体
    return 返回值; // 如果返回类型不是void
}
示例:无返回值函数
void printMessage() {
    cout << "Hello, World!" << endl;
}
示例:带返回值函数
int square(int x) {
    return x * x;
}

2. 函数的参数传递

C++支持多种参数传递方式:

  • 按值传递:传递参数的副本。
  • 按引用传递:传递参数的引用,可以直接修改原值。
  • 按指针传递:传递参数的地址,可以通过指针修改原值。
示例:按引用传递
void increment(int& x) {
    x++;
}

int main() {
    int a = 5;
    increment(a);
    cout << "a = " << a << endl; // 输出 a = 6
    return 0;
}

3. 函数的重载

C++允许函数重载,即定义多个同名函数,只要它们的参数列表不同即可。

示例:函数重载
int add(int a, int b) {
    return a + b;
}

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

int main() {
    cout << add(3, 5) << endl;       // 调用 int 版本
    cout << add(3.5, 2.5) << endl;   // 调用 double 版本
    return 0;
}

4. 内联函数

内联函数是一种优化技术,通过在调用点直接展开函数体来减少函数调用的开销。

示例:内联函数
inline int max(int a, int b) {
    return (a > b) ? a : b;
}

5. 递归函数

递归函数是一种调用自身的函数,通常用于解决分治问题。

示例:递归函数
int factorial(int n) {
    if (n == 0) return 1;
    return n * factorial(n - 1);
}

语法元素描述示例
函数声明指定函数名称、返回类型和参数列表int add(int a, int b);
函数定义包含函数的具体实现int add(int a, int b) { return a + b; }
按值传递传递参数的副本void func(int x) { x++; }
按引用传递传递参数的引用,可以直接修改原值void func(int& x) { x++; }
函数重载定义多个同名函数,参数列表不同int add(int a, int b); double add(double a, double b);
内联函数在调用点直接展开函数体,减少调用开销inline int max(int a, int b) { return (a > b) ? a : b; }
递归函数函数调用自身,用于解决分治问题int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); }
标准库函数C++标准库提供的函数,如sortfindsort(nums.begin(), nums.end());

6. 标准库函数

C++标准库提供了许多内置函数,例如sortfind等。

示例:使用标准库函数
#include <algorithm>
#include <vector>
using namespace std;

int main() {
    vector<int> nums = {3, 1, 4, 1, 5, 9};
    sort(nums.begin(), nums.end()); // 使用 sort 函数排序
    for (int num : nums) {
        cout << num << " ";
    }
    return 0;
}

六、如何调用函数

main函数中,可以创建一个Solution对象并调用threeSum函数:

int main() {
    Solution sol;
    vector<int> num = {-1, 0, 1, 2, -1, -4};
    vector<vector<int>> result = sol.threeSum(num);
    
    for (const auto& triplet : result) {
        for (int num : triplet) {
            cout << num << " ";
        }
        cout << endl;
    }
    
    return 0;
}

七、注意事项

  1. 性能优化:嵌套循环可能导致较高的时间复杂度,应考虑优化算法。
  2. 边界条件:处理空数组或无效输入时,应添加适当的检查。
  3. 代码可读性:函数命名和注释应清晰明了,便于他人理解。

八、总结

函数是编程中不可或缺的工具,掌握其使用方法和最佳实践对于编写高效、可维护的代码至关重要。通过本文的示例和分析,希望读者能够更深入地理解函数的概念和应用,并在实际项目中灵活运用。

关注专栏:每日一题:C++ LeetCode精讲,跟我一起进步。


图片名称
;