您的位置:首页 > 编程语言 > C语言/C++

C/C++结构体内尾部0字节字符数组技巧

2009-01-14 08:58 281 查看
最近工作,学习到一个有意义的C/C++结构体或类的操作技巧,看看如下结构体的定义
//设置字节对齐为1,来反应结构体的真实大小
#pragma pack(push,1)
struct _pack_1
{
//cLen指明cmd指向的缓冲区大小
int cLen;
//在vc中不可以为0,那么设置为1;
char cmd[0];
};

struct _pack_2
{
//cLen指明pCmd指向的缓冲区大小
int cLen;
char *pCmd;
};
#pragma pack(pop)
这个结构体描述了一个简单的网络数据包,而他的长度是不定长的!是不是觉得比较奇怪,这里定义一个char cmd[0]是什么意思、又有什么意义呢?
答案是在这个结构体定义了一个字符数组,他的长度(开销)是0个字节!也就是说sizeof(cmd)为0。
这样写的意义在于表示这个结构体是不定长的,而且要为该结构体分配内存的时候应该要从里动态申请而不能是从栈里分配,简单的说就是只能通过malloc或者new来申请内存缓冲区大小!看下面的例子
//目标缓冲区
char buf_1[]="hello";
//给结构体定长!
size_t pkLen_1=sizeof(_pack_1) + sizeof(buf_1);
//分配堆内存
_pack_1 *pk_1=(_pack_1 *)malloc(pkLen_1);
//设置缓冲区大小
pk_1->cLen=sizeof(buf_1);
//现在访问pk_1->cmd[0] ~ pk_1->cmd[ sizeof(buf_1) - 1 ]都是合法的!
memcpy(pk_1->cmd,buf_1,sizeof(buf_1));
//输出信息
printf("package length is %d/n",pkLen_1);
printf("package's cmd's length is %d/n",pk_1->cLen);
printf("package's cmd content:%s/n",pk_1->cmd);
//注意释放掉所申请的堆内存哦
free(pk_1);
pk_1=NULL;
我们知道,字符数组的数组名是指向其后的缓冲区的首地址,然而我们给该结构体分配内存的时候,多分配了
sizeof(buf_1)个字节,于是现在访问数组名后面的 sizeof(buf_1) - 1 个地址将是合法的!我们把buf1改成指向常量区"My Name is wzoot!!",在不用修改上述代码的情况下,看看输出情况 ^^
这样做是不是很酷?你是否也想到了用字符指针也可以达到一样的目的呢?的确是的,但是考虑下用指针的2个小问题!
首先,一个指针通常是32位的,也就是说,指针本身的开销是4个字节,这比0长度的字符数组开销稍大
其次,如果用指针来表达上述问题,那么还需要一个步骤,那就是要设置指针所指向的缓冲区首地址
pk_2->pCmd=(char *)pk_2+sizeof(_pack_2);
//目标缓冲区
char buf_2[]="hello";
//给结构体定长!
size_t pkLen_2=sizeof(_pack_2) + sizeof(buf_2);
//分配堆内存
_pack_2 *pk_2=(_pack_2 *)malloc(pkLen_2);
pk_2->cLen=sizeof(buf_2);
//在操作之前,需要给指针赋地址
pk_2->pCmd=(char *)pk_2+sizeof(_pack_2);
//现在访问pk_2->pCmd[0] ~ pk_2->pCmd[ sizeof(buf_2) - 1 ]都是合法的!
memcpy(pk_2->pCmd,buf_2,sizeof(buf_2));
//输出信息
printf("package length is %d/n",pkLen_2);
printf("package's cmd's length is %d/n",pk_2->cLen);
printf("package's cmd content:%s/n",pk_2->pCmd);
//注意释放掉所申请的堆内存哦
free(pk_2);
pk_2=NULL;
这样做既麻烦,又可能导致粗心出错,而利用字符数组多方便,而且安全一点,和乐而不为呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: