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.字节对齐 联合的更多相关文章

  1. ARM字节对齐问题详解

    一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...

  2. C语言字节对齐

    转自:http://blog.csdn.net/21aspnet/article/details/6729724 文章最后本人做了一幅图,一看就明白了,这个问题网上讲的不少,但是都没有把问题说透. 一 ...

  3. C语言字节对齐 __align(),__attribute((aligned (n))),#pragma pack(n)

    转载地址 : http://blog.csdn.net/21aspnet/article/details/6729724 一.概念    对齐跟数据在内存中的位置有关.如果一个变量的内存地址正好位于它 ...

  4. C语言中的字节对齐以及其相关处理

    首先,我们来了解下一些基本原理: 一.什么是字节对齐一个基本类型的变量在内存中占用n个字节,则该变量的起始地址必须能够被n整除,即: 存放起始地址 % n = 0,那么,就成该变量是字节对齐的;对于结 ...

  5. pragma pack(非常有用的字节对齐用法说明)

    强调一点: #pragma pack(4) typedef struct { char buf[3]; word a; }kk; #pragma pack() 对齐的原则是min(sizeof(wor ...

  6. c语言,内存字节对齐

    引用:内存字节对齐 写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧. /************* ...

  7. Alignment And Compiler Error C2719 字节对齐和编译错误C2719

    Compiler Error C2719 'parameter': formal parameter with __declspec(align('#')) won't be aligned The ...

  8. stm32中字节对齐问题(__align(n),__packed用法)

    ARM下的对齐处理   from DUI0067D_ADS1_2_CompLib 3.13 type  qulifiers 有部分摘自ARM编译器文档对齐部分  对齐的使用:  1.__align(n ...

  9. 【C语言】字节对齐(内存对齐)

    数据对齐 1.  对齐原则: [原则1]数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma p ...

随机推荐

  1. Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined) B. Code For 1

    地址:http://codeforces.com/contest/768/problem/B 题目: B. Code For 1 time limit per test 2 seconds memor ...

  2. Java并发编程之CountDownLatch的用法

    一.含义 CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.CountDownLatch是一个同步的辅助类,它可以允许一个或多个线程等待, ...

  3. python中统计计数的几种方法

    以下实例展示了 count() 方法的使用方法: 1 2 3 4 5 6 # !/usr/bin/python3   T = (123, 'Google', 'Runoob', 'Taobao', 1 ...

  4. genisoimage命令用法

    功能说明:建立ISO 9660映像文件.  常用命令:genisoimage -o imagename.iso file 语 法:mkisofs [-adDfhJlLNrRTvz][-print-si ...

  5. 基础知识总结之 jdk部分

    第一次安装jdk 按照操作走完  会出现 C:\Program Files\Java\jdk1.8.0_91 和 C:\Program Files\Java\jre1.8.0_91 两个目录 (平级目 ...

  6. python3 使用opencv 画基本图形

    在Python3 环境下安装opencv-python 后练习画基本图形: import numpy as np import cv2 # BGR format GREEN = (0, 255, 0) ...

  7. RabbitMQ学习之(一)_初步了解RabbitMQ、RabbitMQ的使用流程、为什么要使用RabbitMQ、RabbitMQ的应用场景

    初识RabbitMQ RabbitMQ是一个在AMQP协议基础上实现的消息队列系统, 是一个消息代理.它的核心原理非常简单:接收和发送消息.你可以把它想像成一个邮局:你把信件放入邮箱,邮递员就会把信件 ...

  8. hadoop namenode HA集群搭建

    hadoop集群搭建(namenode是单点的)  http://www.cnblogs.com/kisf/p/7456290.html HA集群需要zk, zk搭建:http://www.cnblo ...

  9. 20145312 《Java程序设计》第10周学习总结

    20145312 <Java程序设计>第10周学习总结 学习总结 一. 什么是网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的 ...

  10. 20145314郑凯杰《信息安全系统设计基础》第5周学习总结 part B

    20145314郑凯杰<信息安全系统设计基础>第5周学习总结 part B 在前四天的学习中,我主要对课本知识进行了总结,在本周后三天的学习过程中,我进行实践并截图. http://www ...