编程语言-Cpp

基础知识

关键字

static

const

extern

volatile

register

auto

virtual

delete

default

override

final

explict

friend

内存分布

代码区(Text)

编译器编译源码后集中存放机器指令的区域,需要系统加载到指定内存地址(虚拟地址)才能够正常运行。

  • 存储内容:CPU 执行的机器指令。

  • 特点:只读且共享(如果有多个进程运行同一个程序,它们可以共享内存中的指令段)。

栈区(Stack)

栈由编译器自动分配和释放。它遵循“后进先出”(LIFO)的原则。x86一般向低地址增长。

  • 存储内容:局部变量、函数参数、返回地址。

  • 特点:速度极快,空间有限(通常只有几 MB)。

  • 生存期:随着函数调用的结束,栈帧会被弹出,内存自动回收。

堆区(Heap)

堆是由程序员手动控制的区域。在 C++ 中,我们通过 newmalloc 申请,通过 deletefree 释放。x86一般向高地址增长。

  • 存储内容:动态分配的对象或数组。
  • 特点:空间大(受限于虚拟内存),速度比栈慢,容易产生内存碎片。
  • 生存期:由程序员控制。如果你忘记释放,就会导致内存泄漏

常量区(Constant)

  • 存储内容:常量字符串(如 "Hello World")以及被 const 修饰的全局变量。

  • 特点:只读。尝试修改这部分内存会导致运行时的访问冲突。

全局/静态存储区(Global)

这一块区域在程序启动时分配,程序结束时释放。

  • .data 段:存储已初始化的全局变量和静态变量。

  • .bss 段:存储未初始化(或初始化为 0)的全局变量和静态变量。

    小知识:BSS 段不占用可执行文件实际的大小,只记录所需的空间,在加载时由 OS 清零。

示例代码

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TARGET ?= main
RUN_ARGS ?=

all: $(TARGET)

$(TARGET): main.cpp
@g++ -g -O0 -o $@ $<
run: all
@./$(TARGET) $(RUN_ARGS)
clean:
@rm -f $(TARGET)

.PHONY: all

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

const char *str1 = "Hello world1";
const char *str2 = "Hello world2";

int main(int argc, const char *argv[]) {
cout << "str1 " << &str1 << " " << str1 << endl;
cout << "str2 " << &str2 << " " << str2 << endl;
int v1;
cout << "v1 " << &v1 << endl;
int v2;
cout << "v2 " << &v2 << endl;
auto p1 = new int();
cout << "p1 " << p1 << endl;
auto p2 = new int();
cout << "p2 " << p2 << endl;
return 0;
}

输出信息

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
$ make run  
str1 0x563ce0a0d010 Hello world1
str2 0x563ce0a0d018 Hello world2
v1 0x7ffe5fff6530
v2 0x7ffe5fff6534
p1 0x563cfced32c0
p2 0x563cfced32e0

$ readelf -S main
There are 37 section headers, starting at offset 0x7b20:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.pr[...] NOTE 0000000000000338 00000338
0000000000000030 0000000000000000 A 0 0 8
[ 3] .note.gnu.bu[...] NOTE 0000000000000368 00000368
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000038c 0000038c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003b0 000003b0
0000000000000028 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003d8 000003d8
0000000000000180 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000558 00000558
000000000000019b 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 00000000000006f4 000006f4
0000000000000020 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000718 00000718
0000000000000060 0000000000000000 A 7 2 8
[10] .rela.dyn RELA 0000000000000778 00000778
0000000000000150 0000000000000018 A 6 0 8
[11] .rela.plt RELA 00000000000008c8 000008c8
00000000000000a8 0000000000000018 AI 6 24 8
[12] .init PROGBITS 0000000000001000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 00001020
0000000000000080 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 00000000000010a0 000010a0
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 00000000000010b0 000010b0
0000000000000070 0000000000000010 AX 0 0 16
[16] .text PROGBITS 0000000000001120 00001120
0000000000000385 0000000000000000 AX 0 0 16
[17] .fini PROGBITS 00000000000014a8 000014a8
000000000000000d 0000000000000000 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 00002000
000000000000003c 0000000000000000 A 0 0 4
[19] .eh_frame_hdr PROGBITS 000000000000203c 0000203c
0000000000000044 0000000000000000 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002080 00002080
00000000000000ec 0000000000000000 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003d60 00002d60
0000000000000010 0000000000000008 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003d70 00002d70
0000000000000008 0000000000000008 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003d78 00002d78
0000000000000200 0000000000000010 WA 7 0 8
[24] .got PROGBITS 0000000000003f78 00002f78
0000000000000088 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000004000 00003000
0000000000000020 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000004040 00003020
0000000000000118 0000000000000000 WA 0 0 64
[27] .comment PROGBITS 0000000000000000 00003020
000000000000002d 0000000000000001 MS 0 0 1
[28] .debug_aranges PROGBITS 0000000000000000 0000304d
0000000000000030 0000000000000000 0 0 1
[29] .debug_info PROGBITS 0000000000000000 0000307d
00000000000024b3 0000000000000000 0 0 1
[30] .debug_abbrev PROGBITS 0000000000000000 00005530
00000000000005d0 0000000000000000 0 0 1
[31] .debug_line PROGBITS 0000000000000000 00005b00
00000000000001ab 0000000000000000 0 0 1
[32] .debug_str PROGBITS 0000000000000000 00005cab
0000000000001242 0000000000000001 MS 0 0 1
[33] .debug_line_str PROGBITS 0000000000000000 00006eed
0000000000000289 0000000000000001 MS 0 0 1
[34] .symtab SYMTAB 0000000000000000 00007178
00000000000004b0 0000000000000018 35 21 8
[35] .strtab STRTAB 0000000000000000 00007628
0000000000000387 0000000000000000 0 0 1
[36] .shstrtab STRTAB 0000000000000000 000079af
000000000000016a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)

指针与引用

右值引用

struct与class

struct内存分布

class内存分布

sizeof与strlen

对象的三大特性

封装

继承

多态

虚函数实现动态多态的原理

虚函数与纯虚函数的区别

类的访问权限

private

protected

public

类的成员函数

构造函数

析构函数

赋值函数

拷贝函数

移动构造函数

拷贝构造函数

深拷贝

浅拷贝

类的运算符函数

成员函数

构造函数 析构函数 拷贝构造函数

移动构造函数 移动赋值运算符

运算符(= & + - * / ++ – += -= [] () << >> …)

静态成员函数 static

只读成员函数 const

虚函数 纯虚函数 virtual =0

不可调用函数 =delete

默认类函数 =default

覆写函数 防止覆写函数 override final

单参数构造函数显示转换 explict

友元函数 friend

枚举

类型转换

static_cast

dynamic_cast

const_cast

reinterpret_cast

静态与多态

重写

重载

模板

标准库

std

std::move函数

智能指针

auto_ptr

unique_ptr

shared_ptr

weak_ptr

STL容器

迭代器

迭代器原理

迭代器失效

vector

list

map

set

map与unordered_map

set与unordered_set

vector与list

容器空间配置器

编写技巧

预处理宏自动生成

字符串化

C语言宏定义可以有一些特殊用法:

  • #: 预处理阶段,将宏参数转化为字符串
  • ##: 预处理阶段,将两个标识符拼接成一个标识符

具体步骤:

  1. 将需要的枚举名放到固定的地方统一管理,使用特别的宏函数ENUM_OR_STRING封装,例如枚举文件signal_list.gen
1
2
3
4
5
// signal_list.gen
ENUM_OR_STRING(LED_OPEN), \
ENUM_OR_STRING(LED_CLOSE), \
ENUM_OR_STRING(MSG_TEST), \
ENUM_OR_STRING(MSG_BUTT) \
  1. 定义宏函数ENUM_OR_STRING,使用枚举文件声明枚举
1
2
3
4
5
6
7
8
9
10
11
// signal_id.h
/* 消息ID转枚举 */
#ifdef ENUM_OR_STRING
#undef ENUM_OR_STRING
#endif
#define ENUM_OR_STRING(x) x

typedef enum
{
#include "signal_list.gen"
} E_MSG_ID;
  1. 定义宏函数ENUM_OR_STRING,实现获取枚举字符串方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifdef ENUM_OR_STRING
#undef ENUM_OR_STRING
#endif
#define ENUM_OR_STRING(x) #x

const int MAX_LENGTH_MSG = 50;
const char msgIdString[][MAX_LENGTH_MSG] = {
#include "signal_list.gen"
};

const char *GetMsgName(int msgID)
{
return msgIdString[msgID];
}

常见问题

变量初始化顺序

1
2
3
4
5
6
7
8
9
10
11
[build] /root/project/prj3/xxxx2c/include/MODE2C/DEV/CtxUtility.h: In constructor ‘MODE2C::DEV::CtxComponent::CtxComponent(MODE2C::DEV::NodeArchitecture&, MODE2C::DEV::ASTBasePtr, MODE2C::DEV::NodeComponent&)’:
[build] /root/project/prj3/xxxx2c/include/MODE2C/DEV/CtxUtility.h:137:33: warning: ‘MODE2C::DEV::CtxComponent::mpnewInstance’ will be initialized after [-Wreorder]
[build] MODE2C::DEV::NodeComponent* mpnewInstance;
[build] ^~~~~~~~~~~~~
[build] /root/project/prj3/xxxx2c/include/MODE2C/DEV/CtxUtility.h:136:33: warning: ‘MODE2C::DEV::NodeComponent* MODE2C::DEV::CtxComponent::mpexistingInstance’ [-Wreorder]
[build] MODE2C::DEV::NodeComponent* mpexistingInstance;
[build] ^~~~~~~~~~~~~~~~~~
[build] /root/project/prj3/xxxx2c/src/DEV/CtxUtility.cpp:49:1: warning: when initialized here [-Wreorder]
[build] CtxComponent::CtxComponent(MODE2C::DEV::NodeArchitecture& instanceRoot, ASTBasePtr component,
[build] ^~~~~~~~~~~~
[build] In file included from /root/project/prj3/xxxx2c/src/DEV/CtxUtility.cpp:3:0:

这几句告警说的是变量顺序问题,英文大意为:

成员变量mpnewInstance将会在mpexistingInstance之后初始化,当调用类构造函数CtxComponent::CtxComponent进行初始化时。

解决方案:调整相关变量初始化顺序