应用程序内存基本构成

一个应用程序的内存基本上由:栈区、堆区、数据区(静态存储区)和程序代码区组成。

栈区(Stack Sagment)

在调用函数时,函数内局部变量的存储单元都是在栈上创建,函数执行结束时这些存储单元被操作系统自动被释放。

栈是一块连续的内存区域,大小是操作系统预定好的,Windows下栈大小是2M(也有是1M,在编译时确定,VC中可设置)。

堆区(Heap Sagment)

堆是动态分配内存,分配极限由虚拟内存大小决定,由程序员进行管理。

堆是不连续的内存区域(因为系统是用链表来存储空闲内存地址),堆大小受限于计算机系统中有效的虚拟内存(32bit系统理论上是4G),所以堆的空间比较灵活,比较大。

静态存储区

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

程序代码区

存放函数体的二进制代码。

宏定义原理

  预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。预处理命令以**符号“#”**开头。

  常用的预处理指令包括:

  • 宏定义:#define
  • 文件包含:#include
  • 条件编译:#if、#elif、#ifndef、#ifdef、#endif、#undef
  • 错误信息指令:#error
  • #line指令
  • 布局控制:#pragma

宏定义

  宏定义又称为宏代换宏替换,简称“宏”。宏替换只作替换,不做计算,不做表达式求解。宏定义分带参数的宏定义和不带参数的宏定义。在带参数的宏定义,宏名和参数的括号间不能有空格。

  宏定义不分配内存,变量定义分配内存。

  宏展开不占运行时间,只占编译时间;函数调用占运行时间(分配内存、保留现场、值传递、返回值)。

  出现在宏定义中的**#运算符把跟在其后的参数转换成一个字符串,有时把这种用法的#称为字符串化运算符**。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<cstdio>
using namespace std;
struct Node{
    int val;
    Node* next;
};
void test01() {
    struct Node node = {10,NULL};
    Node node2 = {11,NULL} ;
    Node node3 = {222,NULL};
    node.next = &node2;
    node2.next = &node3;
    Node* cur = &node;
    while(cur != NULL ) {
        printf("node:= %d\n" , cur->val);
        cur = cur ->next;

    }
}

//#define DEBUG 0

int main(void) {
/*
    #ifdef DEBUG
        printf("debug 版本");
    #else
         printf("代码正式版本");
    #endif

*/


    test01();
    return 0;
}

.lib 文件和 dll 文件区别

lib 文件放变量声明, dll 文件放入函数的实现

gcc 工作流程

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

image-20210924193242294

  1. cpp 预处理器 【替换宏之类的】
  2. gcc gcc 自己处理
  3. as 汇编器处理
  4. ld 链接器吹

gcc 命令使用方法

1
2
3
gcc app.c  -I ./include  -o myapp

# -I 表示 我指定的头文件的目录