-
如何学习C++
-
个人理解
- 没什么不一样,就是编译器不一样。
- 编译器就是个字符串解析器,支持
C++ 语法而已。
-
怎么理解
- 后面从
C 的角度上来学习,会涉及到一些编译后的符号,即函数名的说明。
-
符号
-
符号加密规则
-
dlsym
-
-ldl
-
-rdynamic
-
函数重载
-
说明
- 字符串解析就是,编译时加在函数后面达到唯一的新函数.
-
c++ 源码
struct Test{
int a;
};
void show(Test t) {
t.a++;
}
void show(int t) {
t++;
}
int main() {
}
-
解析
g++ test.cpp -g
-
objdump 反编译
000000000040050d <_Z4show4Test>:
struct Test{
int a;
};
void show(Test t) {
40050d: 55 push %rbp
40050e: 48 89 e5 mov %rsp,%rbp
400511: 89 7d f0 mov %edi,-0x10(%rbp)
t.a++;
400514: 8b 45 f0 mov -0x10(%rbp),%eax
400517: 83 c0 01 add $0x1,%eax
40051a: 89 45 f0 mov %eax,-0x10(%rbp)
}
40051d: 5d pop %rbp
40051e: c3 retq
000000000040051f <_Z4showi>:
void show(int t) {
40051f: 55 push %rbp
400520: 48 89 e5 mov %rsp,%rbp
400523: 89 7d fc mov %edi,-0x4(%rbp)
t++;
400526: 83 45 fc 01 addl $0x1,-0x4(%rbp)
}
40052a: 5d pop %rbp
40052b: c3 retq
show(Test) 编译后变成了_Z4show4Test ,_Z 表示加密方式,4 表示后面函数show 名字长度,4Test 表示参数长度.show(int) 编译后变成了_Z4showi ,_Z 表示加密方式,4 表示后面函数show 名字长度,i 表示参数.基本类型被编译器简化了.
-
重复定义
struct Test{
int a;
};
void show(Test t) {
}
void show(int t) {
}
extern "C" void _Z4showi(void) {
}
int main() {
}
- 编译报错,重复定义,因为
_Z4showi 和show(int) 的名字重复了.
-
另类调用
[root@localhost temp]# g++ test.cpp -ldl -rdynamic
[root@localhost temp]# ./a.out
show int: 1
[root@localhost temp]# cat test.cpp
#include<dlfcn.h>
#include<iostream>
struct Test{
int a;
};
void show(Test t) {
}
void show(int t) {
std::cout << "show int: " << t << std::endl;
}
int main() {
void (*t)(int);
*(void**)&t = dlsym(RTLD_DEFAULT,"_Z4showi");
t(1);
}
-
成员函数
-
说明
- 成员函数就是某个类型特定的函数.
- 普通函数添加了一个函数名的前缀,作为专用的函数而已.
-
源代码
#include<dlfcn.h>
#include<iostream>
struct Test{
int a;
Test(int s=0):a(s){}
void show() {
std::cout << "show int: " << a << std::endl;
}
};
int main() {
Test t;
t.show();
}
-
编译执行
[root@localhost temp]# g++ test.cpp -g
[root@localhost temp]# ./a.out
show int: 0
-
反编译
000000000040081d <main>:
Test(int s=0):a(s){}
void show() {
std::cout << "show int: " << a << std::endl;
}
};
int main() {
40081d: 55 push %rbp
40081e: 48 89 e5 mov %rsp,%rbp
400821: 48 83 ec 10 sub $0x10,%rsp
Test t;
400825: 48 8d 45 f0 lea -0x10(%rbp),%rax
400829: be 00 00 00 00 mov $0x0,%esi
40082e: 48 89 c7 mov %rax,%rdi
400831: e8 66 00 00 00 callq 40089c <_ZN4TestC1Ei>
t.show();
400836: 48 8d 45 f0 lea -0x10(%rbp),%rax
40083a: 48 89 c7 mov %rax,%rdi
40083d: e8 70 00 00 00 callq 4008b2 <_ZN4Test4showEv>
}
400842: b8 00 00 00 00 mov $0x0,%eax
400847: c9 leaveq
400848: c3 retq
-
c 风格
[root@localhost temp]# g++ test.cpp -ldl -rdynamic -g
[root@localhost temp]# g++ test.cpp -ldl -rdynamic
[root@localhost temp]# ./a.out
show int: 0
show int: 2
[root@localhost temp]# cat test.cpp
#include<dlfcn.h>
#include<iostream>
class Test{
public:
int a;
Test(int s=0):a(s){}
void show() {
std::cout << "show int: " << a << std::endl;
}
};
int main() {
Test t;
Test b(2);
t.show();
void (*td)(Test*);
*(void**)&td = dlsym(RTLD_DEFAULT,"_ZN4Test4showEv");
td(&b);
}
-
命名空间
-
说明
- 命名空间就是将范围内的所有的变量和函数等,所有有关的都添加一个前缀.
-
命名空间的目的
-
命名空间的扩张
- 两个的前缀都一样,声明了就可以用了.
- 编译器也会这样优先搜索.
-
源代码
[root@localhost temp]# g++ test.cpp
[root@localhost temp]# cat test.cpp
#include<dlfcn.h>
#include<iostream>
namespace cool {
void show(int a) {
std::cout << "cool,show: " << a << std::endl;
}
};
int main() {
}
-
反编译
000000000040081d <_ZN4cool4showEi>:
-
C 风格
[root@localhost temp]# g++ test.cpp -ldl -rdynamic
[root@localhost temp]# ./a.out
cool,show: 2
[root@localhost temp]# cat test.cpp
#include<dlfcn.h>
#include<iostream>
namespace cool {
void show(int a) {
std::cout << "cool,show: " << a << std::endl;
}
};
int main() {
void (*td)(int);
*(void**)&td = dlsym(RTLD_DEFAULT,"_ZN4cool4showEi");
td(2);
}
-
匿名空间
-
不多说
static 一个个的声明太麻烦,{} 匿名的命名空间范围内的所有的都相当于添加了一个static
-
说明
-
总结
|