__packed或者__attribute__((packed))关键字的作用就是用来打包数据的时候以1来对齐
typedef __packed struck test_s {char a; int b;}test_t;
size a = size char = 1
size b = size int = 4
比如某CPU架构的编译器默认对齐方式是4(gaps), size
没pack : 4 + 4 = 8 (gaps最小4)
packed : 1 + 4 = 5 (无gaps)
extern xxx:
只在头文件中做声明(多次include, 多份初始化, 内存地址相同仅因编辑器优化)
全局的变量如果要被引用,就放在*.h中并用extern来声明
如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义(避免重复定义)
使用extern时候要严格对应声明时的格式
extern "C" xxx:
告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名
C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,
可能是fun@aBc_int_int#%$也可能是别的,
不同的编译器采用的方法不一样,为了支持重载
static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.
一般定义static全局变量时,都把它放在原文件中而不是头文件
(多次include, 多份初始化, 内存地址相同仅因编辑器优化)
只限文件内全局用的变量, 加static, 否则就是夸文件全局变量
例如:
include xxx
int a; // exe内有效
static int b; // cpp内有效
{
// 同名变量使用
int b = ::b + 1;
int a = ::a + 1;
}
namespace A {namespace B {int a; ...}}
A::B::a or using namespace A::B
uint8_t\uint_16_t\uint32_t\uint64_t
通过typedef定义的已知的类型的别名, 兼容C、跨平台
注册信号 SIGINT 和信号处理程序:signal(SIGINT, signalHandler);
void signalHandler( int signum )
exit(signum);
生成信号:int raise (signal sig);
union Token {
char c;
int i;
double d;
}
Token tk; // tk占内存为成员最长那个, n选1
============================================================================================================
符号表:
编译器会生成一个叫做“符号表”的数据结构来维护变量名和内存地址直接的对应关系。
它会搜集变量名,
比如我们定义了一个全局的 int a; 那么编译器会为程序预留4个字节(32位平台)的空间,
比如起始地址23456788(长度为4),并把变量名“a”和地址88888888保存进符号表,
这样程序中对a进行相关操作时,它就会根据符号表找到变量的真正的物理位置(23456788),
进行相关操作。
在机器执行程序的时候,会把变量名替换为内存地址(和长度),而不存在任何名称。
============================================================================================================
变量:
您可以把 & 运算符读作"取地址运算符",这意味着,&var 读作"var 的地址"
间接寻址运算符 *,返回操作数所指定地址的变量的值
int 地址 = &var;
int var = *地址;
数组名是指向数组第一个常量的指针:
double dl[10];
double* p = &dl[0]; // p == dl
*(p + n) == dl[n];
char* str地址值 = (int*) str
形参:
值传递:函数参数都是复制进去, 改了不影响源v(v·淡定)
引用递:输入v,用起来还是v, 改了会影响源v(v·危), (大函数里调小函数, 大作用域有效那么小函数就可以流通引用)
指针递:输入v的地址, 用起来是v的地址(真·跑地址), (一些异步调用超出栈, 只能从堆找回分配内存)
例:
int v = 233;
pt(v);
pt(&v);
void pt(int& rv) {
rv : 233
&rv : 0x7fffd102351c
}
void pt1(int* rv) {
*rv : 233
rv : 0x7fffd102351c
&rv : 0x7fffd10234f8
}
同:
int& rv = v;
int*pt = &v;
int*pt1 = (int*)&v;
rv : 233
&v == &rv == pt1 == pt : 0x7fffd102351c
// bzero() 会将内存块(字符串)的前n个字节清零;
// s为内存(字符串)指针,n 为需要清零的字节数。
// 在网络编程中会经常用到。
void bzero(void *s, int n);
============================================================================================================
类:
Line line; // class Line
函数内这么写是新建一个类对象,会调用构造函数,函数结束会被释放,会调用析构函数
此时,line作为参数值传递下去、作为return结果丢出去,都会触发对象复制(超出作用域),这种复制会调用复制构造函数,
情况同Line line2 = line;
也是会触发复制构造函数的复制,也叫赋值初始化
所以可理解项目通用基类nonclone就是防这些情况
inline(小,短,简)
编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,
而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的节省。
this->func() == line.func() == Line::func(&line), 隐藏参数this(常量指针, as Line* of line)
访问控制和继承
访问 public protected private
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no
virtual (动态链接,或后期绑定):
环状继承
A:virtual public D
B:virtual public D
C:public A, public B
运算符重载
Box operator+(const Box&);
Box operator+(const Box&, const Box&);
多态
基类函数virtual int func(...), 派生类多态才生效
纯虚函数virtual int func(...) = 0;
数据封装:把数据和操作数据的函数捆绑在一起的机制
数据抽象:仅向用户暴露接口而把具体的实现细节隐藏起来的机制
抽象类:类中至少有一个函数被声明为纯虚函数
“成员函数指针” 类型不同于 “函数指针”
非静态成员函数有一个隐藏的参数,对应于this指针,该this指针指向的对象的实例。
系统的中断硬件/固件不能提供有关this指针参数。你必须使用“普通”函数(非类成员)或静态成员函数作为中断服务例程。
============================================================================================================
模板:
泛型 == 不讲类型
class or typename:
·在模板定义时的class和typename是没有区别的
·typename可以表示一个嵌套依赖类型名
typedef typename T::LengthType LengthType;
typename C::iterator i = rc.begin();
告诉c++编译器,typename后面的字符串为一个类型名称,而不是成员函数或者成员变量
class A {int a;}
class A {typedef int a;}
"typename A::a i" means the A::a is a type, not a data member.
And "i" is an instance of type A::a
·模板参数只能是class模板
template<typename T, template< typename T> class S>
template <typename T> inline T func(T& p1) {return p1};
func(111) == func<int>(111), func((string)"aabb") == func<string>("aabb")
template <typename T=uint32_t> T默认类型=uint32_t,
相当于实例方func(...) == func<uint32_t>(...)
template <typename T, typename T2, ...> 是个大修饰符, T当行有效
template <class CT> class Stack {public: void func(); ...};
template <class CT> void Stack<CT>::func() {...}
template<>前缀表示这是一个专门化, 描述时不用模板参数, 相当于定类型派生
template <class T> void equip(const T) {...}
template <> void equip(const uint8_t, t1) {...}
template <> void equip(const char_t, t1) {...}
...
// 使用迭代器 iterator 访问值
vector<int>::iterator v = vec.begin();
while( v != vec.end()) v++;
vector.size() : 元素个数
vector.capacity() : 能允许的最大元素数(预分配的内存空间)
push_back, 一般是新增一个元素到vector末尾
vector<int> list1;
默认初始化,vector 为空, size 为0。容器中没有元素,而且 capacity 也返回 0,意味着还没有分配内存空间。
这种初始化方式适用于元素个数未知,需要在程序中动态添加的情况。
vector<int> list2(list);
vector<int> list2 = list;
两种方式等价,list2 初始化为 list 的拷贝。
list 必须与 list2 类型相同,也就是同为 int 的 vector 类型,list2 将具有和 list 相同的容量和元素。
vector<int> list = {1,2,3.0,4,5,6,7};
vector<int> list3 {1,2,3.0,4,5,6,7};
list 初始化为列表中元素的拷贝,列表中元素必须与 list 的元素类型相容。
本例中必须是与整数类型相容的类型,整形会直接拷贝,其他类型会进行类型转换。
vector<int> list3(list.begin() + 2, list.end() - 1);
list3 初始化为两个迭代器指定范围中元素的拷贝,范围中的元素类型必须与 list3 的元素类型相容。
vector<int> ilist4(7);
默认值初始化,list 中将包含7个元素,每个元素进行缺省的值初始化。
对于int,也就是被赋值为0,因此 list4 被初始化为包含7个0。
当程序运行初期元素大致数量可预知,而元素的值需要动态获取的时候,可采用这种初始化方式。
vector<int> ilist5(7, 3)
指定值初始化,ilist5被初始化为包含7个值为3的int。
============================================================================================================
printf中分别代表的输出类型
%a(%A):浮点数、十六进制数字和p-(P-)记数法(C99)
%c:字符
%d:有符号十进制整数
%f:浮点数(包括float和doulbe)
%e(%E):浮点数指数输出[e-(E-)记数法]
%g(%G):浮点数不显无意义的零"0"
%i:有符号十进制整数(与%d相同)
%u:无符号十进制整数
%o:八进制整数 e.g. 0123
%x(%X):十六进制整数0f(0F) e.g. 0x1234
%p:指针
%s:字符串(非空)
%%:"%"
i++ :先引用后增加,先在i所在的表达式中使用i的当前值,后让i加1
++i :先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值