目录
WIN32API文件操作
pragma pack()用法
1、WIN32API文件操作
- 文件的创建和打开
CreateFile函数,可以创建或打开下列对象,返回一个可以读取对象的句柄
文件、管道、邮槽、通信资源、磁盘设备(仅限于Windows NT平台)、控制台、目录(仅适用于打开操作)
HANDLE CreateFile(
LPCTSTR lpFileName,//对象名称
DWORD dwDesiredAccess,//访问方式,GENERIC_READ读访问,GENERIC_WRITE写访问,0设备查询访问
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreateDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
}
dwShareMode:共享方式
0 不共享(独占方式),后续打开操作失败,知道关闭句柄为止
FILE_SHARE_READ 读共享,后续只有使用读共享打开成功,其他失败
FILE_SHARE_WRITE 写共享,后续只有写共享打开成功,其他失败
FILE_SHARE_DELETE 删除访问,后续只有删除访问打开成功,其他失败,将对象标记为待删除,当该对象所有句柄关闭时删除该对象
lpSecurityAttributes:指向一个SECURITY_ATTRIBUTES结构的指针,用来指定创建的文件对象的访问权限,以及返回的句柄能否被子进程所继承。
NULL,使用默认的安全权限
typedef struct _SECURITY_ATTRIBUTES{
DWORD nLength; / /结构体的大小,可用SIZEOF取得,nLength=sizeof(...)
LPVOID lpSecurityDescription;//对象的安全描述符,设置为NULL时为默认安全权限的对象
//返回的对象句柄不能被子进程继承,该安全性意味着只有文件的创建者和管理成员拥有该对象的全部访问权
BOOL bInheritHandle;//安全描述的对象能否被新创建的进程继承
}SECURITY_ATTRIBUTES;
dwCreateDisposition:如何创建文件
CREATE_NEW 新创建文件,若已存在,函数调用失败
CREATE_ALWAYS 新创建文件,若已存在,重写文件并删除现有属性
OPEN_NEW 打开文件,不存在,函数调用失败
OPEN_ALWAYS 打开文件,若不存在,相当于调用CREATE_NEW
TRUNCATE_EXISTING 打开文件,并将文件截取为0字节,必须与GENERIC_WIRTE一起用,若不存在,函数调用失败
GENERIC通用的
dwFlagsAndAttributes:文件属性和标志
FILE_ATTRIBUTE_XXXX
FILE_FLAG_XXXX
以上两种取值,常用的有
FILE_ATTRIBUTES_ARCHIVE 存档文件
FILE_ATTRIBUTES_HIDDEN 隐藏文件
FILE_ATTRIBUTES_NORMAL 没有其他属性设置
FILE_ATTRIBUTES_OFFLINE 文件的数据不能立即使用,该文件已经在物理上移动到离线存储设备上
FILE_ATTRIBUTES_READONLY 制度文件
FILE_ATTRIBUTES_SYSTEM 操作系统文件
FILE_ATTRIBUTE_TEMPORARY 临时万缉拿
hTemplateFile:模板文件
制定一个文件句柄,使用hTemplateFile的属性
必须满足两个条件:创建新文件(CREATE_NEW)+GENERIC_READ打开的
2、pragma pack()用法
语法:#pragma pack( [show] | [push | pop] [, identifier], n )
结构体对齐规则
1)将结构体内所有数据成员的长度值相加,记为sum_a;
2)将各数据成员内存对齐,按各自对齐模数而填充的字节数累加到和sum_a上,记为sum_b。对齐模数是【该数据成员所占内存】与【#pragma pack指定的数值】中的较小者。
3)将和sum_b向结构体模数对齐,指定模数时,该模数是【#pragma pack指定的数值】;未指定模数时,该模数时【系统默认的对齐模数8字节】和【结构体内部最大的基本数据类型成员】长度中数值较小者。结构体的长度应该是该模数的整数倍。
#pragma pack(4)
struct Test1
{
char c;
short sh;
int a;
float f;
int *p;
char *s;
double d;
};
总共占28Bytes。 c的偏移量为0,占1个Byte。sh占2个Byte,它的对齐模数是2(2<4,取小者),存放起始地址应该是2的整数倍,因此c后填充1个空字符,sh的起始地址是2。a占4个Byte,对齐模数是4,因此接在sh后存放即可,偏移量为4。f占4个字节,对齐模数是4,存放地址是4的整数倍,起始地址是8。p,s的起始地址分别是12,16。d占8个字节,对齐模数是4(4<8),d从偏移地址为20处存放。存放后结构体占28个字节,是4的整数倍不用补空字符。
struct Test2
{
char c;
double d;
int a;
short sh;
float f;
int *p;
char *s;
};
将Test1个变量的顺序换一下位置,结构体Test2占用内存32Byte,可见写结构体时,将各个变量按所占内存从小到大排列所占结构体所占内存较小。
关于静态变量static
静态变量的存放位置与结构体实例的存储地址无关,是单独存放在静态数据区的,因此用siezof计算其大小时没有将静态成员所占的空间计算进来
#pragma pack(4)
struct Test3
{
char c;
short sh;
int a;
float f;
int *p;
char *s;
double d;
static double sb;
static int sn;
};
sizeof(Test3)=28
关于类
空类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。
(一)类内部的成员变量:
普通的变量:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。
static修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。
(二)类内部的成员函数:
普通函数:不占用内存。
虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的
不包含虚函数的类
#pragma pack(4)
class CBase1
{
private:
char c;
short sh;
int a;
public:
void fOut(){ cout << "hello" << endl; }
};
不包含虚函数时,对于类中的成员变量按结构体对齐方式处理,普通函数函数不占内存。sizeof(CBase1)=8
包含虚函数的类
#pragma pack(4)
class CBase2
{
private:
char c;
short sh;
int a;
public:
virtual void fOut(){ cout << "hello" << endl; }
};
包含虚函数时,类中需要保存虚函数表的入口地址指针,即需要多保存一个指针。这个值跟虚函数的个数多少没有关系。sizeof(CBase2)=12
子类
子类所占内存大小是父类+自身成员变量的值。特别注意的是,子类与父类共享同一个虚函数指针,因此当子类新声明一个虚函数时,不必在对其保存虚函数表指针入口。
#pragma pack(4)
class CBase2
{
private:
char c;
short sh;
int a;
public:
virtual void fOut(){ cout << "virtual 1" << endl; }
};
class cDerive :public CBase
{
private :
int n;
public:
virtual void fPut(){ cout << "virtual 2"; }
};
sizeof(cDerive)= sizeof(cBase)+sizeof(int n) = 16
回到目录