C++笔记(四): 函数,匿名函数, 数组,C风格的字符串,内联函数

1. C++函数

  • 局部变量最好有一个初始值,不然就会随机了
  • 全局变量自动初始值为0
  • 局部变量也被称之为栈变量,在C++里面需要关心变量的生命周期
  • 栈变量,出栈即消亡,内存中栈变量的储存区
  • 全局变量储存在常量储存区
  • new出来的变量储存在内存中的堆空间,需要自己去控制这个变量的销毁
  • C++11出来了智能指针,去控制变量的销毁
  • 简单看一个函数的case
#include <iostream>

int max(int num1, int num2) {
    // 局部变量声明
    int result = 0;   // 栈变量
    if (num1 > num2) 
        return num1;
    
    else 
        return num2;   // 这里的num1, num2出栈了销毁了,会有另外一个空间去接收他们
}

int main() {
    
    std::cout << "result of max function is: " <<  max(5, 8) << std::endl;
    return 0;
}

函数传值,传引用,指针

  • 传值是在调用的额时候复制了一下,但是可以设置默认值
// 传值 值的传递,复制一份value, 下面main函数调用的时候只是把value复制了一份传过来
// 效率是低的
void swap_value(int x = 5, int y = 10) {
    int temp;
    temp = x;
    x = y;
    y = temp;

    return;
}
  • 传引用的在调用的时候把value绑定在一起,效率高过传值,但是不可以设置默认值
// 传引用, 下面main函数调用的时候,a,x   b, y 的数据是绑定在一起的
void swap_reference(int& x, int& y) {
    int temp;
    temp = x;
    x = y;
    y = temp;
    return;
}
  • 指针: 本身的size是很小的,指向具体对象的地址
    • 可以通过指针找到或者修改一个对象的值,实现访问和修改的功能
    • // 类型 int float map vector
    • // & * ** const static --> 饰词
    • int* a = 10; 的意思是a 是一个int型,同时是一个指针
  • 指针和引用起到的效果看起来差不多
    • 引用要确实传入确定的变量,但是指针可以传一个空指针,空指针可以编译成功,但是会有一个segmentation fault
    • 引用也可以理解为是包了一个指针,他自己会检查是不是都有效
    • 下面的demo包含了传值, 传指针,传引用
// 类型 int float map vector 
// & * ** const static --> 饰词 
void swap_ptr(int* x, int* y){
    int temp = *x;
    *x = *y;
    *y = temp;
    return;
}

完整版的代码如下

#include <iostream>

// 传值 值的传递,复制一份value, 下面main函数调用的时候只是把value复制了一份传过来
// 效率是低的
void swap_value(int x = 5, int y = 10) {
    int temp;
    temp = x;
    x = y;
    y = temp;

    return;
}

// 传引用, 下面main函数调用的时候,a,x   b, y 的数据是绑定在一起的
void swap_reference(int& x, int& y) {
    int temp;
    temp = x;
    x = y;
    y = temp;
    return;
}

// 类型 int float map vector 
// & * ** const static --> 饰词 
void swap_ptr(int* x, int* y){
    int temp = *x;
    *x = *y;
    *y = temp;
    return;
}

int main() {
    int a = 300, b = 500;
    // swap_value(a, b);   
    // std::cout << "swap_value value a is: " << a << std::endl;
    // std::cout << "swap_value value b is: " << b << std::endl;
    // swap_reference(a, b);
    // std::cout << "swap_reference value a is: " << a << std::endl;
    // std::cout << "swap_reference value b is: " << b << std::endl;
    swap_ptr(&a, &b);
    std::cout << "swap_ptr value a is: " << a << std::endl;
    std::cout << "swap_ptr value b is: " << b << std::endl;
    return 0;
}

函数默认参数的注意事项

  • 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数。
  • 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数
//注意点:
//1. 形参b设置默认参数值,那么后面位置的形参c也需要设置默认参数
void TestFunc02(int a,int b = 10,int c = 10){}
//2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数
void TestFunc03(int a = 0,int b = 0);
void TestFunc03(int a, int b){}

lambda函数

  • 一个lambda的案例
 [捕获](传参){具体的实现}
  • 捕获外界变量怎么用
#include <iostream>

void lambda_demo_a() {
    int y = 0;
    int z = 11;
    // 一个lambda的案例 [捕获](传参){具体的实现}
    auto a_lambda_func = [z, &y](int x){
        y = 2 * x;
        return y;
    };
    // 调用之前写好的lambda函数
    std::cout << "lambda demo value: " << a_lambda_func(5) << std::endl;
    return;
}

int main() {
    lambda_demo_a();
    return 0;
}

2. 内联函数

  • 先看看宏

宏的定义

  • 在c中我们经常把一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由是为了执行效率,宏可以避免函数调用的开销,这些都由预处理来完成。
#define ADD(x, y) x + y 

void macro_demo()
{
    int ret1 = ADD(10, 20) * 10;
    cout << ret1 << endl;
}
  • 为什么最终的执行结果是210而不是300呢?原因就是宏定义在预处理的时候是文本替换,所以ret1 = ADD(10, 20) * 10;被展开后就变成了:ret1 = 10+20*10
  • 内联函数为了继承宏函数的效率,没有函数调用时开销,然后又可以像普通函数那样,可以进行参数,返回值类型的安全检查,又可以作为成员函数

内联函数的定义

  • 在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数 。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数

  • 在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。

  • cpp inline void func(int a);

  • 以上写法没有任何效果,仅仅是声明函数,应该如下方式来做:

  • cpp inline int func(int a){return ++a;}

  • 注意: 编译器将会检查函数参数列表使用是否正确,并返回值(进行必要的转换)。这些事预处理器无法完成的。

内联函数注意事项

  • C++中推荐使用内联函数替代宏代码片段

  • 内联函数在最终生成的代码中是没有定义的,C++编译器直接将函数体插入在函数调用的地方,内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)

  • 因为内联函数在最终生成的代码中是没有定义的,所以内联函数的作用域可以理解为只在定义的文件内。假如在a.cpp中定义了inline int func(int a){return ++a;},如果在b.c中需要调用func函数则在b.cpp中需要重新定义内联函数inline int func(int a){return ++a;}

  • inline只是对编译器的一个内联请求,c++内联编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译:

存在任何形式的循环语句

存在过多的条件判断语句

函数体过于庞大

对函数进行取址操作

  • 因此, 内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。

函数默认参数的注意事项

  • 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数。
  • 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数
//注意点:
//1. 形参b设置默认参数值,那么后面位置的形参c也需要设置默认参数
void TestFunc02(int a,int b = 10,int c = 10){}
//2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数
void TestFunc03(int a = 0,int b = 0);
void TestFunc03(int a, int b){}

实现函数重载的条件

  • 同一个作用域
  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同
void func(){}
void func(int x){}
void func(char x){}
void func(int x, int y){}
void func(int x, char y){}
void func(char y, int x){}

3. 数组

  • 所谓数组就是一个集合,里面存放了相同类型的数据元素,连续的内存位置组成的
  • 数组是可以写超的,设置的是10个,也可以遍历他第11个,他也能写出来,但是数值是不可控的
  • C++可以直接操作内存,C++是连续的内存,所以我们拿到的是后面的值
  • 不能像python一样全部展示出来
void array_demo() {
    int n[10]; // 定义一个数组, 是一个包含10个整数的数组
    // 初始化数组元素, 101 - 109
    for (int i = 0; i < 10; i ++) {
        n[i] = i + 100;
    }

    // Element 之后的到value最后的e占据13个
    cout << "Element" << setw(9) << "Value" << endl;
    // 输出数组中每个元素的值
    for (int j=0; j < 11; j++) {
        cout << setw(7) << j << setw(13) << n[j] <<endl;
    }
}
  • 数组和指针的关系
  • 数组就是一个指向第一个元素的指针
  • 下面是实验展示,两个指针地址一样,都可以通过地址+1的方式拿出数组的数据
  • 这里的(p + 1) (array + 1 ) 都是指针的地址 *(地址 取出元素)
// 数组和指针的关系
// 数组和指针的关系
void array_ptr() {
    double array[5] = {11, 21, 31, 41, 51}; 
    double* p;
    p = array;
    cout << "p的地址" << p << endl;
    cout << "array的地址" << array << endl; 
    cout << "索引"  << setw(13) << "数值" << endl;
    for (int i = 0; i < 5; i++) {
        // 两个一样的,这里的(p + 1) (array + 1 ) 都是指针的地址  *(地址 取出元素)
        // cout << setw(2) << i << setw(12) << *(p + i) << endl;  
        cout << setw(2) << i << setw(12) << *(array + i) << endl;
    }
}

4. C风格的字符串

  • 因为C风格的字符串后面有个0\所以定义字符串的时候要 + 1 个单位
void str_len() {
    char str1[4] = "Asd";
    cout << str1 << endl;
}

数组,C风格的字符串的完整版代码

#include <iostream>
#include <iomanip>
#include <cstring>

using namespace std;
using std::setw;

void array_demo() {
    int n[10]; // 定义一个数组, 是一个包含10个整数的数组
    // 初始化数组元素, 101 - 109
    for (int i = 0; i < 10; i ++) {
        n[i] = i + 100;
    }

    // Element 之后的到value最后的e占据13个
    cout << "Element" << setw(9) << "Value" << endl;
    // 输出数组中每个元素的值
    for (int j=0; j < 11; j++) {
        cout << setw(7) << j << setw(13) << n[j] <<endl;
    }
}

// 数组和指针的关系
void array_ptr() {
    double array[5] = {11, 21, 31, 41, 51}; 
    double* p;
    p = array;
    cout << "p的地址" << p << endl;
    cout << "array的地址" << array << endl; 
    cout << "索引"  << setw(13) << "数值" << endl;
    for (int i = 0; i < 5; i++) {
        // 两个一样的,这里的(p + 1) (array + 1 ) 都是指针的地址  *(地址 取出元素)
        // cout << setw(2) << i << setw(12) << *(p + i) << endl;  
        cout << setw(2) << i << setw(12) << *(array + i) << endl;
    }
}

void str_len() {
    char str1[4] = "Asd";
    cout << str1 << endl;
}
int main() {
    // array_demo();
    // array_ptr();
    str_len();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值