C++进阶3.字节对齐 联合
C++进阶3.字节对齐 联合 20131011
多益和金山笔试 知识漏洞 20131011
前言:
今天下午是多益网络的笔试,整体感觉还好,但是找到很多的知识漏洞。一直笔试到6:00,然后紧张的从会生活区嗑了点饭,然后骑车立马冲到华工参加金山的笔试。(华工没有什么认识的人,微信上认识一个看头像和照片挺漂亮的女生,本来想笔试结束的时候见一下,可惜微信她,直到回到学校才回我,下次吧!)回到正题上,笔试上有很多的知识漏洞,所以整理一下。
1.联合的知识
题目是这样的:
union { int val; char str[2];} obj;
main:
obj.str = 10;
obj.str = 1;
cout << obj.val << endl; 先说一下答案, pow(2,8) * 1 + 10 = 266;
首先看一下联合的知识:
联合允许使用多种类型来引用一个对象。联合声明的语法和结构体是一样的,只是语义相差比较大而已,联合中在同一个时间只能存储一个成员数据(即只有一个数据是活跃的),因此当我们访问数据的时候,最多一个数据是正确的。
联合的内存大小是取决于其中字节数最多的成员,而不是累加,联合也会使用字节对其的,联合中存储的数据完全取决于他的解释方式。使用一个成员变量存入数据,然后使用另一个成员变量获取他们的值,但是结果可能不是你想要的。在定义联合变量的时候,可以指定他的初始值,但是只能够指定一个初始值。而且该联合的初始值类型只能够和第一个成员变量类型相匹配。可以获取联合变量的地址,也可以取一个联合变量的任意一个对象的地址,他们总是相等的。
还可以在同类型的变量之间赋值,但是不能够比较两个union变量的大小,不只因为肯能存在填补字节,而且这两个变量可能存储着不同类型的成员。实际上代表了两种不同的变量。
C++中对C中的union进行了扩展,除了数据成员之外,还可以定义成员的访问说明符,也可以定义成员函数,甚至是构造函数和析构函数。但是不可以包含虚拟的成员函数和静态数据成员,不可以作为其他类型的基类或者是派生自其他类型。(也就是处理继承机制、静态、虚拟之类的,其他的关于C++class的都是可以使用的)。
回来通过代码学习联合:
#include <iostream>
using namespace std;
union A{
int val;
char obj[2];
}str;
int main()
{
char ch[2] ={'a','b'};
printf("%c\n" , *ch);
printf("%s\n",ch);
cout << ch << "#end"<<endl;
/*
对于匿名的联合str,初始化的时候将所有的内存置为0,
对于联合直接声明的对象,必须初始化,否则会编译出错,提示没有初始化的联合;
*/
cout << "匿名联合对象 初始化值:" <<str.val << endl;
A feiNiMing;
feiNiMing.obj[0] = 'a';//不写的话,编译出错提示未初始化的联合
cout << "非匿名联合对象初始化值:" << feiNiMing.val << endl;
/*
对联合进行赋值,存储都是从低字节开始,然后放进内存中
*/
str.obj[0] = 10;
str.obj[1] = 1;
cout << str.val << endl; //内存中的四字节是0 0 1 10(从高位到低位) ,然后就是转换成int 为 266
cout << "str.obj[0]:" << (int)str.obj[0] << endl;// 10
cout << "str.obj[1]: " << (int)str.obj[1] << endl;// 1
str.obj[0] = 97;// char 'a'
str.obj[1] = 98;// char 'f'
cout <<"str.obj: " << str.obj << "#end" << endl;// 后面是0 对应的ascii '\0'字符,所以结束输出
cout << endl;
/*所有成员的地址都是联合对象的地址,这些输出是相同的*/
cout << "&str :" << (int)&str << endl;
cout << "&str.val:" << (int)&(str.val) << endl;
cout << "&str.obj:" << (int)&(str.obj) << endl;
cout << endl;
/*通过指针操作联合内存*/
printf("obj[0] address : %d\n", (char*)(&str) );
printf("obj[0] content: %d\n", *(char*)(&str) ); //97
printf("obj[1] address : %d\n", ((char*)(&str)+1) );
printf("obj[1] content: %d\n", *((char*)(&str)+1) );// 98
printf("obj[2] address : %d\n", ((char*)(&str)+2) );
printf("obj[2] content: %d\n", *((char*)(&str)+2) );//0
*((char*)(&str)+1) = 'k';
cout << "str.obj[1]: "<< str.obj[1] << endl;
return 0;
}
2.位域和自己对齐的问题
这一次考试中考到了很多关于sizeof的问题,也就是字节对齐的问题。还有struct 中的数据对齐,位域情况,关于字节对齐,是由一定的了解,但是对于位域,自己从来没有接触过,整理一下。
首先是位域的知识:显然在多数情况下,使用字节为基本单位的存储模式会浪费掉大量的内存空间。如果经常使用bool类型的变量的话,浪费成都会达到87.5%。在进行嵌入式系统开发的时候,提供的内存空间有限,就要对内存空间精打细算了,这种情况下我们可以使用位域和位运算来解决此问题。
位域是以单个的位(bit)为单位设计的一个struct的存储空间,因此可以根据数据成员的有效范围仔细规划他们所需要的位数。
struct DateTime{
unsigned int year ; // 4 byte = 32 bit
unsigned int month :4; // 4 bit < 16
unsigned int day :5; // 5 bit < 32
unsigned int hour :5;
unsigned int minute :6;// 6 bir < 64
unsigned int second :6;
/*后面的数据都是按照bit开始非配的,在最后面的时候,需要考虑对齐 4 + (4+5+5+6+6)/8 取上界 并且对齐 8*/
};
在C中位域的成员只可以是int, unsigned int, signed int;在C++中允许使用的还有char short等等。不允许使用指针类型的或者是浮点数类型的作为位域的数据成员类型.因为可能导致无效的值.此外对于signed int 因为正负号要占用一位,因此该类型的位域成员变量长度至少是2.同时对于unsigned int year :33;也是不正确的,因为超出了范围。
在位域中,同时可以定义非具名的位域成员,作用就是相当于占位符,用于隔离两个相邻的位域成员,但是因为他没有名字,是不可以直接访问的,示例如下:
struct T
{
unsigned int day :5;
unsigned int :2;
unsigned int hour :5;
/*这种占位符仅仅占用了两个bit的空间,将上面两个数据隔开,大小的话是12 bit 对齐的话int 是4 byte*/
};
同时可以定义长度是0 的位域空间,不过这个时候的作用就是让我们的计算机分配内存的时候,从下一个完整的word开始分配内存空间。
struct T
{
unsigned int day :5;
unsigned int :0;
unsigned int hour :5;
/*这种占位符仅仅占用了两个bit的空间,将上面两个数据隔开,从另一个字长分配空间 对齐的话int 是2*4 byte*/
};
另外还有一些需要注意的知识点关于位域的:
1)在修改数据成员的时候,要防止修改位域成员的值时出现的向上溢出的情况;
2)不能够对位域中的数据成员取址,即使该成员完全和字节边对齐,因为字节是编址的最小单位。但是我们可以取位域对象的地址,即使所有成员的位数总和达不到字节的倍数,位域对象也会对齐到机器字长。 即不可以 &t.hour 不可以对位域对象取址;
3)不要把位域对象当做数组,不可以使用[]进行访问。运算最好属于偶那个~,& ! | << >> ^运算符,放置溢出。
前面的DateTime的比较好的设计方案是:
struct DateTime{
unsigned int year ; // 4 byte = 32 bit
unsigned int month :8; // 4 bit < 16
unsigned int day :8; // 5 bit < 32
unsigned int hour :8; s
unsigned int minute :8;// 6 bir < 64
unsigned int second :8;
/*后面的数据都是按照bit开始非配的,在最后面的时候,需要考虑对齐取上界 并且对齐 12*/
};
字节对齐的问题:
本以为自己对于字节对齐十分掌握了,但是今天的多益笔试,让自己彻底挂了,也不叫彻底挂了吧,自己曾经复习过这一类的知识,但是还是错了。所以还是认真细致的整理一遍。
结构成员的对齐方式是否一致,将影响到整个程序运行期间的稳定性和正确性,特别是模块和模块之间的公用的数据类型部分。
typedef unsigned char BYTE;
enum Color{RED = 0x01,BLUE,GREEN,YELLOW,BLACK};
struct Sedan
{
bool m_hasSkylight;
Color m_color;
bool m_isAutoShitf;
double m_price;
BYTE m_seatNum;
/* 考虑字节对齐的话,占用的空间大小是 32 字节*/
};
CPU对对象的访问效率与地址的关系:
对于复合类型的数据结构,如果他的起始地址能够满足其中要求最严格的(最高的)那个数据成员的自然对齐要求,那么他就是自然对齐的;如果有些数据成员也是符合类型的数据结构,则依次类推知道所有的数据成员都是基本的数据类型。
struct Car{
double price;
bool youhuo;
};
struct MyCar
{
char start;
Car name;
bool canRun;
};
这个时候MyCar的大小是按照最严格的的double,是8进行对齐,sizeof(MyCar)是32。都是按照基本类型进行对齐的。
追梦的飞飞
于广州中山大学 2013102
HomePage: http://yangtengfei.duapp.com
C++进阶3.字节对齐 联合的更多相关文章
- ARM字节对齐问题详解
一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...
- C语言字节对齐
转自:http://blog.csdn.net/21aspnet/article/details/6729724 文章最后本人做了一幅图,一看就明白了,这个问题网上讲的不少,但是都没有把问题说透. 一 ...
- C语言字节对齐 __align(),__attribute((aligned (n))),#pragma pack(n)
转载地址 : http://blog.csdn.net/21aspnet/article/details/6729724 一.概念 对齐跟数据在内存中的位置有关.如果一个变量的内存地址正好位于它 ...
- C语言中的字节对齐以及其相关处理
首先,我们来了解下一些基本原理: 一.什么是字节对齐一个基本类型的变量在内存中占用n个字节,则该变量的起始地址必须能够被n整除,即: 存放起始地址 % n = 0,那么,就成该变量是字节对齐的;对于结 ...
- pragma pack(非常有用的字节对齐用法说明)
强调一点: #pragma pack(4) typedef struct { char buf[3]; word a; }kk; #pragma pack() 对齐的原则是min(sizeof(wor ...
- c语言,内存字节对齐
引用:内存字节对齐 写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧. /************* ...
- Alignment And Compiler Error C2719 字节对齐和编译错误C2719
Compiler Error C2719 'parameter': formal parameter with __declspec(align('#')) won't be aligned The ...
- stm32中字节对齐问题(__align(n),__packed用法)
ARM下的对齐处理 from DUI0067D_ADS1_2_CompLib 3.13 type qulifiers 有部分摘自ARM编译器文档对齐部分 对齐的使用: 1.__align(n ...
- 【C语言】字节对齐(内存对齐)
数据对齐 1. 对齐原则: [原则1]数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma p ...
随机推荐
- [转]VMware-Transport(VMDB) error -44:Message.The VMware Authorization Service is not running解决方案
转自:http://blog.sina.com.cn/s/blog_70c9c4b40101i01v.html 1.VMware Workstation中新建的虚拟机在开机的时候出现这种错误:Tran ...
- VS插件神器 ReShaper入门
@简介: 1,Resharper提供以下6个核心功能,分别是: (1). 代码分析(Code Analysis):智能提示代码中存在的问题和修复建议. (2). 编码助手(Coding Assista ...
- Django 部署(Apache下)
前言: 因为需要在服务器下运行python脚本,所以需要搭建Django服务器.所以将自己的学习过程也记录下来,方便日后查阅. 本文环境如下: Ubuntu 16.04 python2.7 Apac ...
- ansible一些基本操作
一.介绍 特性 (1).no agents:不需要在被管控主机上安装任何客户端: (2).no server:无服务器端,使用时直接运行命令即可: (3).modules in any languag ...
- 修改myeclipse字体与操作系统的字体一致
如果你是win7系统,想要修改Myeclipse字体,步骤如下:第一步:C:\Windows\Fonts,找到Courier New,鼠标右键-->显示第二步:Ceneral --> Ap ...
- 搭建Firekylin博客
搭建步骤 1).安装 Node.js curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - yum - ...
- Java学习笔记之对象的复制和克隆
假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short, ...
- 20145324 《Java程序设计》第6周学习总结
20145324 <Java程序设计>第6周学习总结 教材学习内容总结 第十章 1.使用输入串流将数据从来源取出 InputStream 使用输出串流将数据写入目的地 OutStream ...
- Mac与Widow下编译与运行java文件引入多个外部jar包
记录下,以后万一用得着呢 1.MAC环境下: 前提:在终端跳转到当前的源文件目录(cd xx), 并且配置好jdk,这里面不是重点 编译命令:注意连接用 : 号 javac -cp commons ...
- JavaScript对象、JSON对象、JSON字符串的区别
一.首先看下什么是JSON JSON:JavaScript Object Natation,JavaScript对象的表现形式,已经发展成一种轻量级的数据交换格式. JavaScript对象的表现形式 ...