当前位置:天才代写 > tutorial > C语言/C++ 教程 > C语言之C语言的底层操纵

C语言之C语言的底层操纵

2017-11-03 08:00 星期五 所属: C语言/C++ 教程 浏览:397

副标题#e#

概述

C语言的内存模子根基上对应了此刻von Neumann(冯·诺伊曼)计较机的实际存储模子,很好的到达了对呆板的映射,这是C/C++适合做底层开拓的主要原因,别的,C语言适合做底层开拓尚有别的一个原因,那就是C语言对底层操纵做了许多的的支持,提供了许多较量底层的成果。

下面团结问题别离举办叙述。

问题:移位操纵

在运用移位操纵符时,有两个问题必需要清楚:

(1)、在右移操纵中,腾空位是填 0 照旧标记位;

(2)、什么数可以作移位的位数。

谜底与阐明:

">>"和"<<"是指将变量中的每一位向右或向左移动, 其凡是形式为:

右移: 变量名>>移位的位数

左移: 变量名<<移位的位数

颠末移位后, 一端的位被"挤掉",而另一端空出的位以0 填补,在C语言中的移位不是轮回移动的。

(1) 第一个问题的谜底很简朴,但要按照差异的环境而定。假如被移位的是无标记数,则填 0 。假如是有标记数,那么大概填 0 或标记位。假如你想办理右移操纵中腾空位的填充问题,就把变量声明为无标记型,这样腾空位会被置 0。

(2) 第二个问题的谜底也很简朴:假如移动 n 位,那么移位的位数要不小于 0 ,而且必然要小于 n 。这样就不会在一次操纵中把所有数据都移走。

好比,假如整型数据占 32 位,n 是一整型数据,则 n << 31 和 n << 0 都正当,而 n << 32 和 n << -1 都不正当。

留意纵然腾空位填标记位,有标记整数的右移也不相当与除以 。为了证明这一点,我们可以想一下 -1 >> 1 不行能为 0 。

问题:位段布局

struct RPR_ATD_TLV_HEADER
{
ULONG res1:6;
ULONG type:10;
ULONG res1:6;
ULONG length:10;
};

位段布局是一种非凡的布局, 在需按位会见一个字节或字的多个位时, 位布局比按位运算符越发利便。

位布局界说的一般形式为:

struct位布局名{
    数据范例 变量名: 整型常数;
    数据范例 变量名: 整型常数;
} 位布局变量; 

个中: 整型常数必需长短负的整数, 范畴是0~15, 暗示二进制位的个数, 即暗示有几多位。

变量名是选择项, 可以不定名, 这样划定是为了分列需要。

譬喻: 下面界说了一个位布局。

struct{
    unsigned incon: 8; /*incon占用低字节的0~7共8位*/
    unsigned txcolor: 4;/*txcolor占用高字节的0~3位共4位*/
    unsigned bgcolor: 3;/*bgcolor占用高字节的4~6位共3位*/
    unsigned blink: 1; /*blink占用高字节的第7位*/
}ch;

位布局成员的会见与布局成员的会见沟通。

譬喻: 会见上例位布局中的bgcolor成员可写成:

ch.bgcolor 

位布局成员可以与其它布局成员一起利用。 按位会见与配置,利便&节减

譬喻:

struct info{
    char name[8];
    int age;
    struct addr address;
    float pay;
    unsigned state: 1;
    unsigned pay: 1;
}workers;'

上例的布局界说了关于一个工从的信息。个中有两个位布局成员, 每个位布局成员只有一位, 因此只占一个字节但生存了两个信息, 该字节中第一位暗示工人的状态, 第二位暗示人为是否已发放。由此可见利用位布局可以节减存贮空间。

留意不要高出值限制


#p#副标题#e#

问题:字节对齐

我在利用VC编程的进程中,有一次挪用DLL中界说的布局时,觉察布局都乱掉了,完全不能读取正确的值,厥后发明这是因为DLL和挪用措施利用的字节对齐选项差异,那么我想问一下,字节对齐毕竟是怎么一回事?

谜底与阐明:

关于字节对齐:

1、 当差异的布局利用差异的字节对齐界说时,大概导致它们之间交互变得很坚苦。

2、 在跨CPU举办通信时,可以利用字节对齐来担保独一性,诸如通讯协议、写驱动措施时候寄存器的布局等。

三种对齐方法:

1、 自然对齐方法(Natural Alignment):与该数据范例的巨细相等。

2、 指定对齐方法 :

#pragma pack(8) //指定Align为 8;

#pragma pack() //规复到原先值

3、 实际对齐方法:

Actual Align = min ( Order Align, Natual Align )

对付巨大数据范例(好比布局等):实际对齐方法是其成员最大的实际对齐方法:

Actual Align = max( Actual align1,2,3,…)

编译器的填充纪律:

1、 成员为成员Actual Align的整数倍,在前面加Padding。

成员Actual Align = min( 布局Actual Align,设定对齐方法)

2、 布局为布局Actual Align的整数倍,在后头加Padding.

例子阐明:

#pragma pack(8) //指定Align为 8
struct STest1
{
char ch1;
long lo1;
char ch2;
} test1;
#pragma pack()

此刻

Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 )

test1在内存中的分列如下( FF 为 padding ):

#p#分页标题#e#

00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --
01 FF FF FF 01 01 01 01 01 FF FF FF
ch1 -- lo1 -- ch2
#pragma pack(2) //指定Align为 2
struct STest2
{
char ch3;
STest1 test;
} test2;
#pragma pack()

此刻 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 )

test2在内存中的分列如下:

00 — — — 04 — — — 08 — — — 12 — — —

02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF

ch3 ch1 — lo1 — ch2

留意事项:

1、 这样一来,编译器无法为特定平台做优化,假如效率很是重要,就只管不要利用#pragma pack,假如必需利用,也最好仅在需要的处所举办配置。

2、 需要加pack的处所必然要在界说布局的头文件中加,不要依赖呼吁行选项,因为假如许多人利用该头文件,并不是每小我私家都知道应该pack。这出格表示在为别人开拓库文件时,假如一个库函数利用了struct作为其参数,当挪用者与库文件开拓者利用差异的pack时,就会造成错误,并且该类错误很欠好查。

3、 在VC及BC提供的头文件中,除了能正好对齐在四字节上的布局外,都加了pack,不然我们编的Windows措施哪一个也不会正常运行。

4、 在 #pragma pack(n) 后必然不要include其他头文件,若包括的头文件中改变了align值,将发生非预期功效。

5、 不要多人同时界说一个数据布局。这样可以担保一致的pack值。

#p#副标题#e#

问题:按位运算符

C语言和其它高级语言差异的是它完全支持按位运算符。这与汇编语言的位操纵有些相似。 C中按位运算符列出如下:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━

操纵符 浸染

────────────────────────────

& 位逻辑与

| 位逻辑或

^ 位逻辑异或

– 位逻辑反

>> 右移

<< 左移

━━━━━━━━━━━━━━━━━━━━━━━━━━━━

留意:

1、 按位运算是对字节或字中的实际位举办检测、配置或移位, 它只合用于字符型和整数型变量以及它们的变体, 对其它数据范例不合用。

2、 干系运算和逻辑运算表达式的功效只能是1或0。 而按位运算的功效可以取0或1以外的值。 要留意区别按位运算符和逻辑运算符的差异, 譬喻, 若x=7, 则x&&8 的值为真(两个非零值相与仍为非零), 而x&8的值为0。

3、 | 与 ||,&与&&,~与! 的干系

&、| 和 ~ 操纵符把它们的操纵数看成一个为序列,按位单独举办操纵。好比:10 & 12 = 8,这是因为"&"操纵符把 10 和 12 看成二进制描写 1010 和 1100 ,所以只有当两个操纵数的沟通位同时为 1 时,发生的功效中相应位才为 1 。同理,10 | 12 = 14 ( 1110 ),通过补码运算,~10 = -11 ( 11…110101 )。<以几多为一个位序列> &&、|| 和!操纵符把它们的操纵数看成"真"或"假",而且用 0 代表"假",任何非 0 值被认为是"真"。它们返回 1 代表"真",0 代表"假",对付"&&"和"||"操纵符,假如左侧的操纵数的值就可以抉择表达式的值,它们基础就不去计较右侧的操纵数。所以,!10 是 0 ,因为 10 非 0 ;10 && 12 是 1 ,因为 10 和 12 均非 0 ;10 || 12也是 1 ,因为 10 非 0 。而且,在最后一个表达式中,12 基础就没被计较,在表达式 10 || f( ) 中也是如此。

 

    关键字:

天才代写-代写联系方式