__attribute__((packed))详解
1. __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的,我在windows下,用vc的编译器也不是紧凑的,用tc的编译器就是紧凑的。例如:
在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)
在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式)
在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5
2. __attribute__关键字主要是用来在函数或数据声明中设置其属性。给函数赋给属性的主要目的在于让编译器进行优化。函数声明中的__attribute__((noreturn)),就是告诉编译器这个函数不会返回给调用者,以便编译器在优化时去掉不必要的函数返回代码。
GNU C的一大特色就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__书写特征是:__attribute__前后都有两个下划线,并且后面会紧跟一对括弧,括弧里面是相应的__attribute__参数。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束:放于声明的尾部“;”之前。
函数属性(Function Attribute):函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序做到兼容之功效。
GNU CC需要使用 –Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。
packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
如果你看过GPSR协议在TinyOS中的实现,你一定会注意到下面的语句:
typedef struct {
double x;
double y;
} __attribute__((packed)) position_t;
开始我们还可以理解,不久是定义一个结构体嘛!不过看到后面的语句,你可能就会一头雾水了,’ __attribute__((packed))’是什么东西?有什么作用?一连串的疑问马上就会从你脑袋里冒出来。虽然这个对理解整个程序没有什么影响,但我不想让这些疑问一直呆在我的脑子里,负担太重。省得以后念念不忘,而且也许有一天可以用的上呢。搞清楚这个问题吧!
GNU C的一大特色(却不被初学者所知)就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束为:放于声明的尾部“;”之前。
packed是类型属性(Type Attribute)的一个参数,使用packed可以减小对象占用的空间。需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。
使用该属性对struct或者union类型进行定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型(it indicates that the smallest integral type should be used)。
下面的例子中,my-packed-struct类型的变量数组中的值会紧凑在一起,但内部的成员变量s不会被“pack”,如果希望内部的成员变量也被packed的话,my-unpacked-struct也需要使用packed进行相应的约束。
struct my_unpacked_struct
{
char c;
int i;
};
struct my_packed_struct
{
char c;
int i;
struct my_unpacked_struct s;
}__attribute__ ((__packed__));
在每个系统上看下这个结构体的长度吧。
内存对齐,往往是由编译器来做的,如果你使用的是gcc,可以在定义变量时,添加__attribute__,来决定是否使用内存对齐,或是内存对齐到几个字节,以上面的结构体为例:
1)到4字节,同样可指定对齐到8字节。
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((aligned(4)));
2)不对齐,结构体的长度,就是各个变量长度的和
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((packed));
跨平台时基于数据结构的网络通信
有时为了提高程序的处理速度和数据处理的方便,会使用基于数据结构的通信(不需要对流进行解析)。但是,当需要在多平台间进行通信时,基于数据结构的通信,往往要十分注意以下几个方面:
[1] 字节序
[2] 变量长度
[3] 内存对齐
在常见的系统架构中(Linux X86,Windows),非单字节长度的变量类型,都是低字节在前,而在某些特定系统中,如Soalris Sparc平台,高字节在前。如果在发送数据前不进行处理,那么由Linux X86发向Soalris Sparc平台的数据值,势必会有极大的偏差,进而程序运行过程中无法出现预计的正常结果,更严重时,会导致段错误。
对于此种情况,我们往往使用同一的字节序。在系统中,有ntohXXX(), htonXXX()等函数,负责将数据在网络字节序和本地字节序之间转换。虽然每种系统的本地字节序不同,但是对于所有系统来说,网络字节序是固定的-----高字节在前。所以,可以以网络字节序为通信的标准,发送前,数据都转换为网络字节序。
转换的过程,也建议使用ntohXXX(), htonXXX()等标准函数,这样代码可以轻松地在各平台间进行移植(像通信这种很少依赖系统API的代码,做成通用版本是不错的选择)。
变量的长度,在不同的系统之间会有差别,如同是Linux2.6.18的平台,在64位系统中,指针的长度为8个字节,而在32位系统中,指针又是4个字节的长度---此处只是举个例子,很少有人会将指针作为数据发送出去。下面是我整理的在64位Linux系统和32位Linux系统中,几种常见C语言变量的长度:
short int long long long ptr time_t
32位 2 4 4 8 4 4
64位 2 4 8 8 8 8
在定义通信用的结构体时,应该考虑使用定常的数据类型,如uint32_t,4字节的固定长度,并且这属于标准C库(C99),在各系统中都可使用。
内存对齐的问题,也与系统是64位还是32位有关。如果你手头有32位和64位系统,不妨写个简单的程序测试一下,你就会看到同一个结构体,即便使用了定常的数据类型,在不同系统中的大小是不同的。对齐往往是以4字节或8字节为准的,只要你写的测试程序,变量所占空间没有对齐到4或8的倍数即可,举个简单的测试用的结构体的例子吧:
struct student
{
char name[7];
uint32_t id;
char subject[5];
};
在每个系统上看下这个结构体的长度吧。
内存对齐,往往是由编译器来做的,如果你使用的是gcc,可以在定义变量时,添加__attribute__,来决定是否使用内存对齐,或是内存对齐到几个字节,以上面的结构体为例:
1)到4字节,同样可指定对齐到8字节。
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((aligned(4)));
2)不对齐,结构体的长度,就是各个变量长度的和
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((packed));
转自http://blog.chinaunix.net/uid-25768133-id-3485479.html
__attribute__((packed))详解的更多相关文章
- __attribute__ 机制详解(一)
GNU C 的一大特色就是__attribute__ 机制.__attribute__ 可以设置函数属性(Function Attribute).变量属性(Variable Attribute)和类型 ...
- __attribute__ 机制详解
GNU C 的一大特色就是__attribute__ 机制.__attribute__ 可以设置函数属性(Function Attribute).变量属性(Variable Attribute)和类型 ...
- (转)__attribute__之section 分析详解
原文地址:__attribute__之section详解 前言 第一次接触 "section" 是在公司的一个STM32的项目代码中,前工程师将所有的初始化函数都使用的" ...
- __attribute__ 详解
GNU C的一大特色(却不被初学者所知)就是__attribute__机制.__attribute__可以设置函数属性(Function Attribute).变量属性(Variable Att ...
- ip头、tcp头、udp头详解及定义,结合Wireshark抓包看实际情况
公司的同事们在分析网页加载慢的问题,忽然使用到了Wireshark工具,我就像发现新大陆一样好奇,赶紧看了看,顺便复习了一下相关协议.上学时学的忘的差不多了,汗颜啊! 报文封装整体结构 mac帧头定义 ...
- 从gcc的__attribute__((packed))聊到结构体大小的问题
公司的前辈的代码里面 结构体的花括号最后 有__attribute__((packed))字样.以前没见过,所以查了查.学习学习http://blog.sina.com.cn/s/blog_559f6 ...
- [转]Redis内部数据结构详解-sds
本文是<Redis内部数据结构详解>系列的第二篇,讲述Redis中使用最多的一个基础数据结构:sds. 不管在哪门编程语言当中,字符串都几乎是使用最多的数据结构.sds正是在Redis中被 ...
- Linux下usb设备驱动详解
USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...
- USB协议枚举过程详解
一 枚举过程之文字描述 ?主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察.(集线器端口的两根信号线的每一根都有15kΩ的下拉电阻,而每一个设备在D+都有一个1.5kΩ的上拉电阻.当用US ...
随机推荐
- 一步一步学WebSocket(二) 使用SuperWebSocket实现自己的服务端
上一篇文章,我们了解了客户端如何与服务器创建WebSocket连接.但是一个巴掌拍不响,既然是通信,就必然最少要有两个端.今天我们来看看c#如何用已有的框架实现一个WebSocket服务端. 在.Ne ...
- LoadRunner ---协议分析
在做性能测试的时候,协议分析是困扰初学者的难题,选择错误的协议会导致Virtual User Generator 录制不到脚本:或录制的脚本不完整,有些应用可能需要选择多个协议才能完整的记录 客户端与 ...
- npm 换源
npm config set registry https://registry.npm.taobao.org
- Linux下如何查看系统启动时间和运行时间
1.uptime命令输出:16:11:40 up 59 days, 4:21, 2 users, load average: 0.00, 0.01, 0.002.查看/proc/uptime文件计算系 ...
- spring mvc 4.3.2 + mybatis 3.4.1 + mysql 5.7.14 +shiro 幼儿园收费系统 之 登录
如标题,用spring mvc 4.3.2+mybatis 3.4.1 + mysql 5.7.14 +shiro 开发了一个用于幼儿园的管理系统. 功能模块 包括 账号,角色,权限管理. 幼儿档案管 ...
- asp.net连接数据库
Asp.net web连接数据库步骤. 一. 新建一个web工程. 1.文件->添加->新建网站->asp.net web网站Winform窗体. 2.新建好的网站最下面有 ...
- PPTP VPN 限制一个账号只允许一个用户来登录
创建auth-up文件 vi /etc/ppp/auth-up chmod a+x /etc/ppp/auth-up auth-up脚本内容如下 #!/bin/sh # get the usernam ...
- web测试安全性常见问题
web测试安全性常见问题 一. 登录账号明文传输 1. 问题一:登录账号密码或者修改密码明文传输 现象:目前物流对内的java系统基本上都是 ...
- Endless Sky源码学习笔记-2
数据载入框架: void GameData::BeginLoad(const char * const *argv)为数据载入的最上层method,其主要框架为: void Files::Init(c ...
- MongoDB Replica Set 选举过程
什么是选举? 选举是副本集选择某个成员成为primary的过程.primary是一个副本集中唯一能够接收写操作的成员. 下面的事件能够引发一次选举: 第一次初始化一个副本集 Primary失效.rep ...