Cpp语法1

C++初级语法

  • Cpp整个语法的核心都在围绕封装权限进行发展
    • 因此我们看待Cpp绝大语法的使用 要从封装权限两个角度进行理解

引子

  • 初阶Cpp主要是解决C中的不足而设计
  • C++重要的成熟版本分别为:C++98C++11
  • CC++的应用场景:主要是服务端开发然后是嵌入式C++一般适用于底层开发
  • C++特性
    1. C++语法兼容C语法
  • 前置知识:标识符是用来命名变量、函数、类、结构体、枚举、命名空间等用户定义实体的名称
    • 也就是说变量名、类名、函数名、枚举类型名、结构体名、命名空间名、...都属于标识符

C++关键字

  • C++的关键字是在C的基础上新增了32个关键字

C++关键字大全

cpp 复制代码
asm        do           if               return      try      continue
auto       double       inline           short       typedef  for
bool       dynamic_cast int              signed      typeid   public
break      else         long             sizeof      typename throw
case       enum         mutable          static      union    wchar_t
catch      explicit     namespace        static_cast unsigned default
char       export       new              struct      using    friend
class      extern       operator         switch      virtual  register
const      false        private          template    void     true
const_cast float        protected        this        volatile while
delete     goto         reinterpret_cast

命名空间

  • C存在标识符命名冲突的问题

C 语言命名冲突演示

报错结果:ERROR命名冲突报错,出现重复定义

问题出现原因:核心在于:同名标识符不可以出现在同一个域 ,因此<stdlib.h>内部的rand函数名就会和用户定义的rand变量名在全局域下出现重名冲突

c 复制代码
#include<stdlib.h>
#include<stdio.h>

int rand=10;      //这里会报错,命名冲突错误

int main()
{
    return  0;
}
  • Cpp解决方案:引入命名空间,本质就是引入新的域,冲突的标识符分别被不同的命名空间包含起来,通过将冲突的标识符分别封装在不同的域,完美的规避了命名冲突
    • 和命名空间处于同一作用域的代码要想使用命名空间内的标识符

Cpp解决方案演示

cpp 复制代码
#include<stdlib.h>
#include<stdio.h>

namespace Bit
{
    int rand=10;
}

int main()
{
    printf("%d\n",Bit::rand);
    return 0;
}
  • 命名空间域理解
    • 封装角度:命名空间可以理解为对指定资源的封装
    • 权限角度:对 命名空间内资源的标识符 进行 权限设计
    • 使用角度
      1. [域名]::[标识符]的方式,编译器会直接使用命名空间中资源的标识符控制资源
      2. 同一作用域下 并且 using namespace [域名]下方的代码 才可以直接使用 命名空间内资源标识符控制资源
  • 命名空间特性
    1. 不会改变所包含对象的生命周期,不会改变所包含对象的生命周期性质,没包含之前是生命周期是什么包含之后依旧是不变
    2. 当同一作用域下定义了两个同名命名空间,这两个命名空间就会被合并
    3. 命名空间内可以定义函数、类、对象、等资源
      • 命名空间内部的函数、类、对象 这三者对应的标识符不冲突 就可以随便定义
  • 扩展:命名空间内可以继续定义命名空间

编译器访问对应对象优先级

  • 编译器默认查找原则:向上查找,编译器执行到对应代码时寻找资源时,会根据此代码的访问权限先访问此代码 所处在的域 进行向上查找,没找到 就向上去包含此域的域内继续向上查找,逐渐向上一层一层查找,直至全局域内向上查找完为止,就不向外查找了
  • 如果对应的标识符被指定对应的域了,那么编译器直接去指定的域进行获取标识符对应的对象、函数、类 执行后续操作了

标识符被指定域进行使用

  • [域名]::[标识符]表示 这个代码有权限 并且 指定去该命名空间 使用此标识符
  • 指定全局域方式:::[标识符]
  • 指定命名空间域方式[命名空间]::[标识符]
  • 指定类域方式:[类名]::[标识符]

展开命名空间

  • 展开语法:using namespce [命名空间域];
  • 命名空间外面且下方位置 和命名空间 处于同一作用域的指令 可以直接 获取和使用该命名空间的标识符
    • 展开命名空间后,编译器会默认 认为展开名空间这段代码 下面位置且被同一作用域包住 的所有代码 有权限 直接使用命名空间内资源的标识符 进行操作
  • 展开命名空间慎用

只展开命名空间的指定标识符

  • 展开语法:using namespace [命名空间域]::[标识符]

知名命名空间

  • 全局作用域下存在一个知名命名空间叫做std命名空间,这个命名空间包含C++绝大部分标准方法、对象、类

C++输入和输出

  • C语言的标准输入和标准输出需要printf进行指定类型,Cpp优化方案:设计了cincout自动识别类型,免去手动指定类型

cout 和 cin

  • coutcin存在于iostream头文件里
  • coutcin都属于输入流对象,这些对象用于输入输出操作

cout

  • coutCpp用于标准输出的对象,是ostream类型的对象
  • Cpp中的cout<<进行了复用,<<和内置类型的对象一起使用就是左移操作,但在Cpp这里cout搭配<<就是标准输出的操作
  • 一个cout搭配多个<<就是连续标准输出
  • 语法:cout << [可打印的数据]

cout 使用

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
  cout << "hello world" << '\n';
  return 0;
}

cin

  • cinCpp用于标准输入的对象,是istream类型的对象
  • Cpp中的cin>>进行了复用,>>和内置类型的对象一起使用就是右移操作,但在Cpp这里cin搭配>>就是标准输入操作
  • 一个cin搭配多个>>就是连续标准输入
  • 语法:cin >> [被赋值的对象]

cin 使用

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
  int a;
  cin >> a;
  return 0;
}

缺省参数

  • C调用自定义函数必须调用时填写实参,Cpp优化方案:定义自定义函数时,给指定的形参提供缺省值,当用户不给指定的形参传递实参数据时,默认该形参的缺省值就会填充给该形参

缺省参数使用

cpp 复制代码
#include<iostream>
using namespace std;

void Func(int a = 0)
{
  cout << "a = " << a << endl;
}

int main()
{
  Func(10);   //不触发缺省参数,标准输出为 a = 10
  Func();     //触发缺省参数,标准输出为 a = 0
  return 0;
}

缺省参数的规则

  • 当用户自定义函数时,给形参填写缺省参数时,必须从最右至左,连续的填写缺省参数
  • 用户提供的实参数据,从自定义函数的参数列表的最左至右依次传递给自定义函数的形参
  • 缺省参数:必须是常量或者全局变量

缺省参数正确使用

cpp 复制代码
int Func(int a,int b,int c=2)
int Func(int a,int b=1,int c=2)
int Func(int a=0,int b=1,int c=2)

缺省参数错误的使用

cpp 复制代码
int Func(int a,int b=1,int c)
int Func(int a=0,int b,int c)
int Func(int a=0,int b,int c=2)

函数根据缺省进行的分类

  • 全缺省:自定义的函数的所有参数都存在缺省值
  • 半缺省:自定义的函数部分参数存在缺省值

声明和参数分离场景下的缺省参数用法

  • 函数声明和定义分离的情况下,缺省参数规定 缺省参数只能在声明位置提供
    • 声明定义分离就是只函数的声明和函数的定义不在同一个文件下,函数声明和函数调用在一个文件,函数定义单独一个文件
  • 如果不在声明位置给缺省参数,那么项目会在编译器的编译阶段报错,因为调用函数的代码触发了缺省参数,编译器会进行识别,发现声明位置不存在缺省参数,编译器就会报错

编译器生成可执行程序的四个阶段

预处理

  • 预处理阶段:会对.cc.c进行展开头文件、宏替换、条件编译、去注释操作
  • 形成的文件为.i文件

编译阶段

  • 编译阶段:会对预处理形成后的.i文件进行语法检查,然后生成对应的汇编指令放到.s文件中
  • 语法检查中函数检查过程中,编译器会在主函数所在下 搜索与调用函数的代码匹配的函数头,看看调用函数的代码是否符合函数头的返回值和参数
    • 这个函数头 就是 主函数所在文件下 定义的函数声明
  • 编译阶段会为每个.i文件形成符号表,符号表会记录当前.i文件下所有的自定义函数地址函数名+参数类型的映射关系

符号表

cpp 复制代码
Func(int,int)  :  0AA12F211
Test()         :  1FDD76551
Func(char,int) :  9ADD21985

汇编

  • 将汇编代码进行解析,解析成二进制机器码放到.o文件中

链接

  • 将上阶段的.o文件合并成一个可执行文件,编译阶段的所有符号表也会在此阶段合成一张符号表 供给 可执行程序

声明定义分离场景下的编译器的四个阶段

cpp 复制代码
 ______________________        _________________________     ___________________
|Func.h                |      |Func.cc                  |   |Main.cc            |
| void Func(int a=10); |      |void Func(int a)         |   |#include"Func.h"   |
|______________________|      |{                        |   |int main()         |
                              |   cout<<"a ="<<a<<endl; |   |{                  |
                              |}                        |   |  Func(1);         |
                              |_________________________|   |  return 0;        |
                                                            |}                  |
                                                            |___________________|

预处理

Main.cc 头文件展开

cpp 复制代码
 ________________________      _________________________
|Main.i                  |    |Func.i                   |
|void Func(int a=10);    |    |void Func(int a)         |
|int main()              |    |{                        |
|{                       |    |   cout<<"a ="<<a<<endl; |
|   Func(1);             |    |}                        |
|   return 0;            |    |_________________________|
|}                       |
|________________________|

编译

语法检查形成汇编代码,因为Main.i存在函数声明,因此可通过语法检查

因为Main.i只有声明没有定义,Main.i通过了语法检查,编译器对调用函数对应的汇编指令暂时设为call Func(?),后续在链接阶段?会变成函数地址

顺便根据每个文件的全部函数形成对应的符号表,每个文件对应一个符号表

cpp 复制代码
 ______________________________    ______________________________________  :.o文件
|Main.o                        |  |Func.o                                |
|movl       $10, %esi          |  |0A01357h    jmp   Func(int)(0A01770h) |
|00A0191F   call Func(int)(?)  |  |                                      |
|......                        |  |void  Func(int a)                     |
|______________________________|  |{                                     |
                                  |0A01770   push    ebp                 |
                                  |0A01771   mov     ebp,esp             |
                                  |0A01773   push    esp,0C0h            |
                                  |...                                   |
                                  |}                                     |
                                  |______________________________________|
          _____________________                     _____________________    :符号表
         |Func(int):?          |                   |Func(int) : 0A01770h |
         |_____________________|                   |_____________________|

汇编

这里就是将Main.oFunc.o转成两个.s文件,内部逻辑没变

我们这里进行反汇编,观察到这两个文件和上一阶段没任何区别,所以就不用在伪代码模拟了

  • 省略

链接

这里会将.s两个文件合并成一个可执行程序文件,多个文件的符号表会合成一张大的符号表
call Func(?)部分编译器会查询符号表进行对应地址的补充

为了可观性这里进行反汇编表达

cpp 复制代码
 ______________________________________
|exe                                   |
|Main()                                |
|{                                     |
|...                                   |
|00A0191F   call   Func(int)(0A01770h) |:? 被替换成 真函数地址
|...                                   |
|}                                     |
|...                                   |
|0A01357h    jmp   Func(int)(0A01770h) |
|...                                   |
|void  Func(int a)                     |
|{                                     |
|0A01770   push    ebp                 |
|0A01771   mov     ebp,esp             |
|0A01773   push    esp,0C0h            |
|...                                   |
|}                                     |
|...                                   |
|______________________________________|

函数重载

  • C中存在一个函数名只能对应一个函数定义,这样就太单一了,C++解决方案:设置函数重载,函数名相同只要参数不同,就可以定义多个同名函数
  • 函数名相同,参数类型不同 或者 参数类型顺序不同,都可构成函数重载
  • 注意:只有返回值不同无法构成重载

函数重载使用

一下四个函数都可以构成重载

cpp 复制代码
void Func(int a)
{
  cout << endl;
}

void Func(int a,char b)
{
  cout << endl;
}

void Func(char b,int a)
{
  cout << endl;
}

void Func()
{
  cout << endl;
}

C++支持函数重载的原理

  • C编译器区分函数是单纯通过函数名来进行区分,Cpp编译器区分函数是通过函数名+函数的参数类型进行区分
    • 此时同一作用域就可以定义相同函数名的不同函数了
  • Cpp函数名+参数类型来标识一个函数

引用

  • C中通过指针变量来控制指向的对象比较繁琐,Cpp解决方案:引入引用类型,给标识符对应的资源或者单纯的资源添加新的标识符,直接对新的标识符操作就可以控制对应的资源
  • Cpp&进行扩展复用,类型结合&形成[类型]&就是引用类型
  • 只要新标识符和原标识符不在同一作用域就可以相同

引用的使用

这里是给a标识符对应的资源添加了新的标识符bab标识符对应的是同一份内存资源

cpp 复制代码
#include<iostream>

int main()
{
  int a=0;
  int& b=a;       //b是a对应资源的另一个标识符
  cout<<&a<<endl;
  cout<<&b<<endl;
}

引用做参数

  • 引用做参数,形参名就变成了实参对象的新标识符,Cpp会为引用类型的形参 生成指针对象 来维护 形参名 作为 实参对象 的新标识符,此时形参的开销仅仅为指针大小,引用类型作为形参类型 有效控制了形参的开销,如果形参类型 是实参类型,当实参对象过大时,形成的形参对象开销也会变大

引用做参数

这里引用做参数,直接用新的别名来控制对应的资源,大大提高了可观可用性

引用做参数,那么这个参数叫做输出型参数

输出型参数 可以 将函数内部的处理结果,通过参数送出函数 被外部接收并使用

cpp 复制代码
void Swap(int& a,int& b)        //a是i的别名,b是j的别名
{
  int c=a;
  a=b;
  b=c;
}
int main()
{
  int i=1;
  int j=2;
  cout<<"i="<<i<<"j="<<j<<endl;
  Swap(i,j);
  cout<<"i="<<i<<"j="<<j<<endl;
  return 0;
}

引用特性

  • 引用特性
    1. 引用必须被初始化,也就是说[引用类型] [标识符];这个代码是错误的,必须[引用类型] [标识符]=[资源];
    2. 引用不能改变指向,也就是说引用一旦成为了某个资源的标识符,那么该引用在当前作用域会一直都作为这个资源的标识符
  • 引用和指针是相辅相成的,虽然引用的产生是为了解决指针部分带来的不便,其不是为了替代指针而产生的

引用底层原理

  • Cpp引用类型底层采用的是C中的指针实现出来的,编译器会对 引用对象名 在底层就是创建一个指针变量 来维护和实现 引用对象名 作为新标识符 的行为

引用类型做返回值类型

  • C中,函数返回都会产生保存返回值对象数据的临时变量,此时产生的临时对象就是内存开销,当大型结构体类型为返回值类型,那么开销也会随之变大,成本变高,Cpp解决方案:使用引用类型作为返回值类型
  • 函数返回时,一定会产生临时变量,当临时变量类型不是引用类型时,这个临时变量就会被开辟成返回值类型大小的空间,然后存储函数返回值对象的数据,函数(参数)这段调用代码 也会变成这个临时变量的标识符,当返回值类型为引用类型时,这个临时变量就变成了指针变量,存储返回值对象的地址,对地址进行引用层面维护,让函数(参数)这段调用代码 变成 返回值对象 的新标识符
  • 注意:引用作为返回类型,那么作为返回值的对象 通常是 静态、全局、堆上的,函数(参数)这段代码也会变成返回值对象的新标识符,如果返回值对象被回收了,那么这个新标识符对应的内存空间就是非法空间

C 演示大型结构体做返回值类型的开销

此时内存就会开辟一个sizeof(struct BIG)大小的空间,这个空间就是临时变量,也是一种内存开销

c 复制代码
struct BIG
{
  int a;
  bool b;
  char* c;
  double d;
}

struct BIG Func()
{
  struct BIG big;
  return big;
}

int main()
{
  struct BIG b=Func();  //此时会产生临时对象接受这个big对象,这个临时对象就是很大的开销
  return 0;
}

C中,函数返回值逻辑图

cpp 复制代码
 __________________________ :内存
|   ______________________    :Func函数栈帧
|  |return [struct BIG big]     :big对象作为返回值
|  |___________|__________
|          data copy
|              |
|              V
|          [struct BIG]        :[struct BIG]表示临时生成的临时变量,用于接收big对象的数据,这个临时变量大小为 sizeof(struct BIG)
|   ________________|______    :main函数栈帧
|  |                |
|  |[struct BIG b]=Func();      :b是main函数中用于接收[struct BIG]内部数据的对象,Func()会成为[struct BIG]的标识符
|  |______________________
|___________________________

Cpp 解决 C 中函数返回值出现开销的问题

Cpp引用的底层是指针变量,产生的临时变量是struct BIG&类型,这个临时变量内存会开辟struct BIG*指针来维护这个引用对象,虽然都开辟了响应的内存空间,但指针类型相比于大结构类型,开销此时就大大减少了

cpp 复制代码
#include<iostream>
struct BIG
{
  int a;
  bool b;
  char* c;
  double d;
}

struct BIG& Func()
{
  struct BIG* big=(struct BIG*)malloc(sizeof(struct BIG));
  return *big;
}

int main()
{
  struct BIG b=Func();
  return 0;
}

Cpp中,函数返回值逻辑图

txt 复制代码
 __________________________     :内存
|        ______________________    :Func函数栈帧
|       |return [struct BIG big]     :big对象作为返回值
|       |___________________|__
|                           |
|   [struct BIG*]-maintain->|      :[struct BIG*]表示临时生成的指针变量,用于维护Func()这段代码 作为big对象新标识符的行为,这个临时指针变量大小为 sizeof(struct BIG*)
|        ___________________|___   :main函数栈帧
|       |                   |
|       |[struct BIG b]=Func();      :b是main函数中用于接收[struct BIG]内部数据的对象,Func()作为[struct BIG]的标识符
|       |______________________
|___________________________

const 关键字

  • const用于修饰标识符,使用 被修饰的标识符 是无法修改其对应的对象内部数据的
  • 临时变量具有常性,意思就是 临时变量对应的标识符 会被const修饰
  • 定义时,const只会修饰 *+标识符的组合 或者 标识符
    • const本质就是修饰标识符的,*+标识符的组合 本质就是一个 新的标识符

内联函数

  • C中函数调用是存在开销的,每次调用就要创建一次函数栈帧,Cpp解决方案:内联函数,执行到调用内联函数位置不进行创建函数栈帧了,而是编译时直接将此函数逻辑在调用位置直接展开,展开在调用位置所处的栈帧中
  • 虽然C中宏函数使用后的效果跟内联函数相同,但宏函数操作复杂使用性差并且debug下无法被调适,因此Cpp就产生了内联函数
  • 函数过大被inline修饰,编译器并不会将此内联函数展开,因为函数过大,调用位置展开后就会导致形成的代码段变大,此时展开的开销大于创建的开销,编译器就会避免这种情况,不展开
    • 代码段过大(代码膨胀)就会导致执行效率变慢,代码段大(代码膨胀)对应的可执行程序也会很大这空间也会出现损耗
  • 内联函数不支持声明定义分离,并且可以被展开的内联函数是不会被加入到符号表中的

C 中,不断创建函数栈帧场景

此场景会创建大量函数栈帧,对内存的占用和CPU的调度有很大的消耗

c 复制代码
#include<stdio.h>
int gval=1;
void Func()
{
  printf("%d\n",++gval);
}
int main()
{
  for(int i=0;i<=100;i++)
  {
    Func();
  }
  return 0;
}

Cpp 解决方案:内联函数

此时Func函数的逻辑会被编译器解析时,将Func逻辑展开在main函数中

cpp 复制代码
#include<iostream>
using namespace std;

inline void Func()
{
  printf("%d\n",++gval);
}
int main()
{
  for(int i=0;i<=100;i++)
  {
    Func();
  }
  return 0;
}

.h 文件包含函数定义,并且被多个.c 文件包含,函数表重定义解决方案

普通的函数定义 被放到.h 文件,存在链接时符号表合并后函数名冲突问题

txt 复制代码
 ____________________    ___________________    ____________________
|Func.h              |  |Func.c             |  |Main.c              |
|void Func()         |  |#include"Func.h"   |  |#include"Func.h"    |
|{                   |  |                   |  |#include<stdio.h>   |
|  printf("Func\n"); |  |                   |  |int main()          |
|}                   |  |                   |  |{                   |
|____________________|  |___________________|  |   return 0;        |
                                               |}                   |
                                               |____________________|
                           |  预处理:头文件展开
                           V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|void Func()         |  |void Func()         |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|

                        |  编译:形成符号表
                        V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|void Func()         |  |void Func()         |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|
                                                :符号表
 _______________         ________________
|Func():0xfffff |       |Func():0x0000f  |
|_______________|       |________________|

                        |  汇编
                        |  链接:合并符号表,此时报错,合并后的符号表出现重名函数
                        V
  • 解决方案:static 修饰的函数定义
  • static修饰后的函数,这个函数的链接属性会被改变,这个函数只被预处理后,其所处的.cc/.c文件自己可用,其他文件访问不到这个static修饰的函数
    • 本质就是被static修饰的函数不会放到符号表,但可直接被使用,可执行程序只使用主函数文件的 static 修饰函数

static 修饰函数解决方案

txt 复制代码
 ____________________    ___________________    ____________________
|Func.h              |  |Func.c             |  |Main.c              |
|static void Func()  |  |#include"Func.h"   |  |#include"Func.h"    |
|{                   |  |                   |  |#include<stdio.h>   |
|  printf("Func\n"); |  |                   |  |int main()          |
|}                   |  |                   |  |{                   |
|____________________|  |___________________|  |   return 0;        |
                                               |}                   |
                                               |____________________|
                           |  预处理:头文件展开
                           V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|static void Func()  |  |static void Func()  |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|

                        |  编译:形成符号表,因为Func被static修饰所以不会放到符号表,并且每个.c的static修饰函数只会被该.c自己使用
                        V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|static void Func()  |  |static void Func()  |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|
                                                :符号表
 _____________________   ______________________
|OtherFuncA():0x3427ff| |OtherFuncB():0x9826ef |
|_____________________| |______________________|

                        |  汇编
                        |  链接:合并符号表
                        V
                 ___________
                |Execute    |
                |___________|

Cpp 解决方案:内联函数函数

内联函数函数凭借展开时,不会加入符号表的特性完美的解决了,符号表函数名冲突的问题

内联函数函数可展开时,其只在预处理后其所在的文件可用

  • Cpp 的 inline 函数解决方案
txt 复制代码
 ____________________    ___________________    ____________________
|Func.h              |  |Func.c             |  |Main.c              |
|inline void Func()  |  |#include"Func.h"   |  |#include"Func.h"    |
|{                   |  |                   |  |#include<stdio.h>   |
|  printf("Func\n"); |  |                   |  |int main()          |
|}                   |  |                   |  |{                   |
|____________________|  |___________________|  |   return 0;        |
                                               |}                   |
                                               |____________________|
                           |  预处理:头文件展开
                           V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|inline void Func()  |  |inline void Func()  |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|

                        |  编译:形成符号表,因为Func被inline修饰所以不会放到符号表,并且每个.c的inline修饰函数只会被该.c自己使用
                        V
 ____________________    ____________________
|Func.c              |  |Main.c              |
|inline void Func()  |  |inline void Func()  |
|{                   |  |{                   |
|  printf("Func\n"); |  |  printf("Func\n"); |
|}                   |  |}                   |
|____________________|  |...                 |
                        |int main()          |
                        |{                   |
                        |   return 0;        |
                        |}                   |
                        |____________________|
                                                :符号表
 _____________________   ______________________
|OtherFuncA():0x3427ff| |OtherFuncB():0x9826ef |
|_____________________| |______________________|

                        |  汇编
                        |  链接:合并符号表
                        V
                 ___________
                |Execute    |
                |___________|

auto 关键字

  • C 中定义变量都需要具体类型 标识符 = 值的形式,Cpp为了省劲就借鉴了Python,直接auto 标识符=值
    • auto 是很灵活的推导
  • 自动根据 赋的值 来 推导标识符的类型
  • auto 无法做函数的参数和函数的返回值,但 C++14 后 auto 可以做函数的返回值类型,auto 不可以定义数组
  • auto 的弊端就是当你自己本身不了解返回类型时,你就无法使用返回值,auto 存在的意义就是你清楚值的类型使用基础上直接用auto,你要不清楚值的类型就会很糟糕

auto 关键字的使用

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
  int i=0;
  auto p1=i;    //auto->int
  auto& p2=i;   //auto&->int&
  auto* p3=&i;  //auto*->int*
  auto p4=&i;   //auto=int*
  return 0;
}

auto 关键字衍生的范围 for 语法

  • Cppfor循环高级用法

范围 for 使用演示

for(auto e:arr),auto 类型的 e 会自动依次从循环中取 arr 数组的元素对 e 初始化,然后取完就自动结束循环

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
  int arr[10]={1,2,3,4,5,6,7,8,9,10};
  for(auto e:arr)
  {
    printf("e\n");
  }
  return 0;
}

nullptr

  • Cpp98 中的 NULL 是 0 的 define,并不是 C 中的 NULL(空指针),因此 Cpp11 中就设置了 nullptr 来代表 C 中的 NULL

权限

  • const修饰的标识符属于读权限,反之没被const修饰的标识符就具有读写权限
  • 读写权限的指针或引用类型 的标识符 不可以被 读权限的标识符 初始化,这种行为叫做权限不可被放大
相关推荐
艾莉丝努力练剑1 小时前
静态地址重定位与动态地址重定位:Linux操作系统的视角
java·linux·运维·服务器·c语言·开发语言·c++
王老师青少年编程2 小时前
2025年3月GESP真题及题解(C++七级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·信奥赛·七级
sycmancia3 小时前
C++——多态
开发语言·c++
像污秽一样3 小时前
算法设计与分析-算法效率分析基础-习题1.1
c语言·数据结构·c++·算法
2401_8846022712 小时前
程序人生-Hello’s P2P
c语言·c++
初中就开始混世的大魔王13 小时前
2 Fast DDS Library概述
c++·中间件·信息与通信
娇娇yyyyyy13 小时前
C++基础(6):extern解决重定义问题
c++
Neteen14 小时前
【数据结构-思维导图】第二章:线性表
数据结构·c++·算法
灰色小旋风14 小时前
力扣——第7题(C++)
c++·算法·leetcode