Skip to content

zcmiracle/cplusplus_study

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cin、函数重载、extern "C"、cout、默认参数

cin、cout

  • C++中常使用cincout进行控制台的输入、输出
    • cin用的右移运算符 >>
    • cout用的左移运算符 <<
    • endl换行
  • getchar()等待键盘输入,如果敲回车,就会读取键盘输入

函数重载

  • 规则:
    • 函数名相同
    • 参数个数、参数顺序、参数类型不同
  • 注意
    • 返回值类型与函数重载无关,会造成歧义,直接报错
    • 调用函数时,实参的隐式类型转换可能会产生二义性
  • 本质:
    • 采用了name mangling或者叫name decoration技术
    • C语言并不会根据参数进行改编、修饰,所以C语言不支持函数重载
    • C++编译器默认会对 符号名(函数名)进行改编、修饰
    • 重载时会生成多个不同的函数名,不同编译器(MSVC、g++)有不同的生成规则

默认参数

  • C++允许函数设置默认参数,在调用时可以根据情况省略实参。

    • 默认参数只能按照右到左的顺序
    • 如果函数同时有声明、实现,默认参数只能放在函数声明中
    • 默认参数的值可以是常量、全局符号(全局变量、函数名)
    • C语言也可以将函数名作为参数传递,只是没有默认参数
  • 函数重载、默认参数可能会产生冲突、二义性(建议优先使用默认参数)

extern "C"

  • extern "C"修饰的代码会按照C语言的方式去编译

  • 如果函数同时有声明和实现,要让函数声明被extern "C"修饰,函数实现可以不修饰

  • 由于CC++编译规则的不同,在C、C++混合开发时,可能会出现以下操作:

    • 在C++调用C语言API时候,需要使用extern "C"修饰C语言函数声明
  • 编写C语言代码中直接使用extern "C",这样可以直接被C++调用,但是如果C语言调用就会报错,所有用以下方式:

    • 通过宏 __cplusplus来区分C、C++环境
    #ifdef __cplusplus
    extern "C" {
    #ifdef __cplusplus
    
    int sum(int a, int b);
    
    #ifdef __cplusplus
    }
    #ifdef __cplusplus

#pragma once

  • 使用 #ifndef#define#endif防止头文件的内容被重复包含

  • #pragma once可以防止整个文件的内容被重复包含

  • 区别:

    • #ifndef#define#endif受C/C++标准库的支持,不受编译器的任何限制
    • 有些编译器不支持#pragma once,兼容性不够好
    • #ifndef#define#endif可以只针对一个文件中的部分代码,而#pragma once只能针对整个文件

内联函数(inline function)

  • 使用inline修饰函数的声明或实现,可以使其变成内联函数
    • 建议声明 和 实现都增加 inline修饰
  • 特点
    • 编译器会将函数调用直接展开为函数体代码
    • 可以减少函数调用的开销
    • 增大代码体积
  • 注意:
    • 尽量不要内联超过10行代码的函数
    • 有些函数即使声明为inline,也不一定被编译器内联,比如递归函数 optimization level

内联函数与宏

  • 内联函数和宏,都可以减少函数调用的开销
  • 对比宏,内联函数多了语法检测函数特性

表达式

  • C++ 有些表达式是可以被赋值
int a = 10;
int b = 20;
// a = 30
(a = b) = 30;
// 30 > 20 所以 b = 100
(a < b ? a : b) = 4;

const

  • const是常量的意思,被其修饰的变量不可修改
    • 如果修饰的是结构体的指针,其成员也不可以更改
  • const修饰的是其右边的内容

引用(Reference)

  • C语言中,使用指针(Pointer) 可以间接获取、修改某个变量的值
  • C++中,使用引用(Reference)可以起到跟指针类似的功能。
int age = 20;
// refAge 就是一个引用
int &refAge = age;
  • 注意点
    • 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
    • 对引用做计算,就是对引用所指向的变量做计算。
    • 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”。
    • 可以利用引用初始化另一个引用,相当于多个别名
    • 不存在引用的引用、指向引用的指针、引用数组
  • 引用存在价值:比指针更安全、函数返回值可以被赋值

引用的本质

  • 引用的本质就是指针(通过汇编分析,指针和引用的汇编代码一样),只是编译器削弱了它的功能,所以引用就是弱化了的指针
  • 一个引用要占用一个指针的大小。通过结构体里面设置指针和引用分析得出。
int main() {
    int age = 10;
    // 指针
    int *p = &age;
    *p = 20;
    return 0;
}

int main() {
    int age = 10;
    // 引用
    int &refAge = age;
    refAge = 30;
    return 0;
}
  • 指针和引用生成的汇编代码如下:
// 指针
`main:
    pushq  %rbp
    movq   %rsp, %rbp
    xorl   %eax, %eax
    movl   $0x0, -0x4(%rbp)
    movl   $0xa, -0x8(%rbp)
    leaq   -0x8(%rbp), %rcx
    movq   %rcx, -0x10(%rbp)
    movq   -0x10(%rbp), %rcx
    movl   $0x14, (%rcx) // 16 + 4 = 20
    popq   %rbp
    retq

// 引用
`main:
    pushq  %rbp
    movq   %rsp, %rbp
    xorl   %eax, %eax
    movl   $0x0, -0x4(%rbp)
    movl   $0xa, -0x8(%rbp)
    leaq   -0x8(%rbp), %rcx
    movq   %rcx, -0x10(%rbp)
    movq   -0x10(%rbp), %rcx
    movl   $0x1e, (%rcx) // 16 + 14 = 30
    popq   %rbp
    retq

Releases

No releases published

Packages

No packages published