三块核心内容

进程虚拟地址空间区域划分

有四区

  1. 代码区(.text) 存放程序的机器指令,通常是只读的
  2. 数据段(.data) 存放已初始化的全局变量和静态变量
  3. BSS段 存放未初始化全局变量和静态变量,操作系统在运行前会帮助自动初始化为0
  4. 堆栈区(Heap and Stack)
    • 堆区:程序运行时动态分配的内存(new 和 malloc),由程序员管理
    • 栈区:函数调用时使用的内存,存放局部变量和返回地址,由系统自动分配和释放
1
2
3
4
5
6
int main()
{
int a = 12
int b = 0;
int c;
}

这三条是其实是汇编的mov指令,存放在.text区,而main函数调用的时候会在栈开辟空间。

函数调用堆栈详细过程

程序编译链接原理

预处理-编译-汇编-链接

  1. 预处理 处理#include 之类的,除了#pragma lib和#pragma link,这两个是在链接时处理。
  2. 编译 gcc / g++(gcc用来编译c语言,g++用来编译c++,其实就是dev中的F9,只不过现在换成命令行形式) 在编译的过程中符号不分配虚拟地址,在链接分配。
  3. 汇编 符号表的生成,生成.o文件。.o文件为目标文件(Object File),目标文件是一个二进制文件,文件的格式是ELF(executable and linkable file)
  4. 链接 将各个段合并(比如main.o和sum.o)生成可执行文件,Linux下是.out/Windows下是.exe
    • 步骤一:所有的.o文件段合并,符号表合并后,进行符号解析(要找到所有符号表引用的定义)。
    • 步骤二:符号的重定向(让UND找到初始定义的位置)这是链接的核心,符号解析成功后,给所有的符号分配虚拟地址!

g++/gcc语法

  • gcc -c / g++ -c 是编译 compile


    使用objdump -t查看符号表(t - table)
    使用cat查看文件内容
  • 在main.cpp中,我们看的定义gdata是一个外部引用的变量,sum是声明,看main.o的符号表,他们并不是没有符号,符号是UND(undefined),这个意思是:我现在在当前代码上用到他们了,但是我却不知道他们是怎么定义的,UND是对于符号的引用,有确定数据段的是符号的定义需要在链接这一部分才能让他们找到“家”,main函数放在.text代码段上,我们定义了data,所以data放在了.data段
  • 在sum.o中,因为sum.cpp定义好了每一个位置,所以符号表都有。

    文件头,main.o 。 .o文件就是由各种各样的段来组成的

    可以使用objdump -s xxx.o查看段的信息
    ld是链接器,负责链接。
  • ELF文件头记录着文件的入口点地址,程序就知道从第几行开始执行了。

总结 a.out和.o文件都是由各种段组成的,但区别在a.out多了一个#program headers段,有两个load,告诉程序在运行的时候需要加载哪些数据。

C++基础部分

形参带默认值的函数

1
2
3
4
5
6
7
8
9
10
11
12
int sum(int a = 20, int b )
{
return a + b;
}

int main()
{
int a = 10;
int b = 20;
int ret = sum(a, b);
cout << ret;
}

这个代码是错误的,因为在函数传参压入栈的时候,参数是由右往左压,现在b没有默认值,所以是错的,如果定义b为20,a不定义,代码是正确的。代码顺序:从上到下,从右到左给默认值

内联函数inline

/* inline内联函数和普通函数的区别???

  • inline内联函数:在编译过程中,就没有函数的调用开销了,在函数的调用点直接把函数的代码进行展开处理
  • inline函数不再生成相应的函数符号(objdump -t)
  • inline只是建议编译器把这个函数处理成内联函数,但是不是所有的inline都会被编译器处理成内联函数,比如递归,如果有符号说明没有内敛
  • debug版本上,inline是不起作用的;inline只有在release版本下才能出现
    */
    函数调用的过程:
1
2
3
4
5
6
7
8
9
10
11
12
int sum(int a , int b = 20)
{
return a + b;
}

int main()
{
int a = 10;
int b = 20;
3. int ret = sum(a, b);
cout << ret;
}

在这里,第三行参数从右向左压栈,然后调用call指令,call 会把返回地址(main 函数中调用 sum 后的下一条指令)压入栈顶,然后跳转到 sum。要开辟函数栈帧,栈帧包含:返回地址、保存的寄存器、局部变量空间、对齐填充等。调用完后栈帧销毁,返回地址出栈,跳回 main 继续执行。虽然就是简简单单的x+y操作(三行汇编:mov add mov),但是如果循环了100000次呢?每一次都要这样开辟,很耗内存。
inline int sum(int x,int y)
{
return x+y;
}
inline函数建议编译器内联展开函数(这是个建议,而不是强制),比如在第三行,很有可能编译成int ret = a+b而不是调用函数。

函数重载

/*

  1. c++为什么支持函数重载,c语言不支持函数重载
    • C++代码产生函数符号的时候,是由函数名+参数列表组成的;C产生函数符号的时候,只由函数名组成。
      可以理解为C++:cmp_int_int / cmp_double_double / cmp_char *_char *,而c就会发生链接错误。
  2. 函数重载需要注意什么
  3. c++和c语言代码之间如何互相调用 (为何无法调用?因为函数符号不同,一个有列表一个没列表,会无法连接,UND
    • C调用C++:无法直接调用,把c++源码扩在extern “C”
    • C++调用C:把C函数的声明扩在extern “C”中
      */
      什么是重载函数?
  4. 一组函数,其中函数名相同,参数列表的个数或类型不同,那么这一组函数就称为 函数重载
  5. 一组函数要称得上重载,一定先是处在同一个作用域中。
  6. const / volatile,是怎么影响形参类型的。
  7. 一组函数,函数名相同,参数列表也相同,仅仅是返回值不同,不叫重载 因为函数名参数列表都相同

const的用法

  1. const与普通变量的区别
    • const修饰的变量不能够再作为左值!!!初始化完成后,值不能被修改。
    • 不能把常量的地址泄露给一个普通的指针或普通的引用变量 不能int p = const int
  2. c和c++中const的区别是什么?
    const的编译方式不同,c中,const就是当作一个变量来编译生成指令的。
    C++中,所有出现const常量名字的地方,都被常量的初始化替换了!!!所以在C++中使用const必须要初始化。比如const int a = 20,接下来的int arr[a],这个a不是a了,而是20。那使用指针有没有修改掉a的值呢?有,已经改掉了。
  3. c++的const必须初始化,叫常量。如果用变量为const定义的量赋值,就叫常变量(和C一样了),因为初始值不是立即数,是一个变量,这时候printf出来的都会是变量。
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main()
{
int b = 20;
const int a = b;
int* p = (int*)&a;
*p = 30;
cout << a << " " << *(&a)<<" "<<*p;
}

const与一级指针/二级(多级)指针的结合

c++语言规范:const修饰离它最近的类型
const右边如果没有指针,是不参与类型的*
const和一级指针的结合

  • ①const int p //修饰int,说明p的值不能被改,但是p可以指向其他地方,一和二一样,const附近都是int
  • ②int const *p
  • ③int *const p //最靠近const的类型是:int *,修饰指针,说明指针指向不能改,但值可以改

总结const指针和指针类型的转换公式 理解:把一个普通指针变为const修饰的指针,告诉编译器:我不会再修改它了,是正确的

  • int* <= const int* 是错误的
  • const int* <= int* 是正确的

new/malloc delete/free

  1. delete和free的区别?malloc和free称作C的库函数,new和delete是运算符。
  2. new不仅可以开辟内存,还可以初始化内存;malloc只负责开辟内存大小的空间,返回的指针是void *,所以要强制类型转换。 c++int *p = (int*)malloc(sizeof(int)*20);
  3. malloc开辟失败,是通过返回值和nullptr作比较;new开辟失败,通过抛出异常
  4. new有几种?
    • int *p = new int(20);
    • int *p2 = new (nothrow) int; //不抛出异常
    • const int *p3 = new const int(40);
    • int data = 0;int *p4 = new (&data) int(50); //定位new,在指定的位置赋值

类和对象

OOP语言的四大特征:抽象 封装/隐藏 继承 多态
类:属性->成员变量 行为->成员方法
通过访问限定符体现:public private protected