字节顺序
字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;大端字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。
基于X86平台的PC机是小端字节序的,而有的嵌入式平台则是大端字节序的。因而对int、uint16、uint32等多于1字节类型的数据,在这些嵌入式平台上应该变换其存储顺序。通常我们认为,在空中传输的字节顺序即网络字节序为标准顺序,考虑到与协议的一致以及与同类其它平台产品的互通,在程序中发数据包时,将主机字节序转换为网络字节序,收数据包处将网络字节序转换为主机字节序。
16位的转换函数:ntohs和htons
ntohs(network to host short)是将网络字节顺序转换为主机字节顺序,返回值是一个16位的整数,即2个字节长度的整数short int,也可以写作uint16_t。
htons(host to network short)是将主机字节顺序转换为网络字节顺序,返回值也是一个16位的整数short int。
32位转换函数ntohl和htonl。
ntohl(network to host long)是将网络字节顺序转换为主机字节顺序,返回值是一个16位的整数,即2个字节长度的整数long int,也可以写作uint32_t。
htonl(host to network long)是将主机字节顺序转换为网络字节顺序,返回值也是一个16位的整数long int。
inet_addr函数可以将一个点分十进制的IP地址转换成网络字节顺序的长整形十进制值。inet_ntoa函数将网络字节顺序的长整形十进制数值的IP地址转换成点分十进制。
堆(heap)栈(stack)
在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
栈(操作系统):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
堆(数据结构):列队优先,先进先出,堆可以被看成是一棵树,如:堆排序
栈(数据结构):先进后出(First-In/Last-Out)
一个由C/C++编译的程序占用的内存分为以下几个部分:
- 栈区 —— 由编译器自动分配释放 ,存放函数的参数名,局部变量的名等,速度较快,但程序员是无法控制的。在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
- 堆区 —— 由程序员分配释放,一般速度比较慢,容易产生内存碎片,若程序员不释放,程序结束时可能由OS回收。堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
- 全局区(静态区) —— 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
- 文字常量区 —— 常量字符串就是放在这里的,程序结束后由系统释放 。
- 程序代码区 —— 存放函数体的二进制代码。
//main.cpp int a = 0;//全局初始化区 char *p1;//全局未初始化区 main() { int b;//栈 chars[] = "abc";//栈 char *p2;//栈 char *p3 = "123456";//123456\0在常量区,p3在栈上。 static int c = 0;//全局(静态)初始化区 p1=(char*)malloc(10); p2=(char*)malloc(20);//分配得来的10和20字节的区域就在堆区。 }
函数调用约定
函数调用约定,是指当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。
几种类型:__stdcall,__cdecl,__fastcall,__thiscall,__nakedcall,__pascal
Win32 API函数绝大部分都是采用__stdcall调用约定的。WINAPI其实也只是__stacall的一个别名而已(#define WINAPI __stdcall)。在函数的面前用__stdcall作为修饰符。此时函数将会采用__stdcall调用约定,int __stdcall sumExample (int a, int b)。使用__stdcall调用约定生成的可执行文件要比__cdecl的要小,因为在每次的函数调用都要产生堆栈清理的代码,而栈的清理(堆栈平衡操作)是由被调用函数执行的。
函数具有可变参数像我wsprintf这个函数,与前面的prinf一样,都必须使用__cdecl调用约定,因为只有调用者才知道参数的数量在每一次的函数调用,因此也只有调用者才能够执行堆栈清理操作。