笔试知识-c变量

32bit,64bit 变量长度

类型 32bit 64bit
char 1 1
float 4 4
int 4 4
short 2 2
double 8 8
long 4 8
long long 8 8
void * 4 8
long double 12 16

结构体的对齐

结构体大小的计算方法和步骤: - 数据类型自身的对齐值,对于char,自对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4字节。 - 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值 - 指定对齐值:#pragma pack(value)指定对齐value. - 数据成员,结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

注意:结构体或类中的静态成员不对结构体或类的大小产生影响。因为其储存在.data 没有成员变量的结构体或类的大小为1,因为必须保证让每一个实例在内存中有唯一的地址。

例子

#pragma pack(2)
struct s1
{
       char a;          //1
       int b;           //2*2
       short c;         //2
}
/**************************************/

[]*  _____char a
* *
* *  _____int b
* *  _____short c

sizeof(s1) == 8;
#pragma pack(8)
struck s2
{
      char a;       //1
      int  b;       //4
      short c;      //2

}

struck s3
{
      struck s2 s2;
      char b;
      int c
}

sizeof(s2) == 12

sizeof(s3) == 20

联合体

  • 1.联合体是一个结构
  • 2.它的所有成员相对于基地址的偏移量为0
  • 3.结构体空间要大到足够容纳最宽的成员
  • 4.其对齐方式要适合其中所有成员
  • 5.大小能被包含所有基本数据类型大小所整除
/*
 * =====================================================================================
 *
 *       Filename:  union.c
 *
 *    Description:  联合体测试程序
 *
 *        Version:  1.0
 *        Created:  2014年04月01日 17时04分13秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  izobs (Lin), ivincentlin@gmail.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <stdio.h>

typedef union U1
{
    char s[9];
    int n;
    double d;
}U1;

typedef union U2
{
    char s[5];
    int n;
    double d;
}U2;


int main(int argc, char *argv[])
{
    U1 u1;
    U2 u2;
    printf("%d\n",sizeof(u1));
    printf("%d\n",sizeof(u2));
    printf("0x%x\n",&u1);
    printf("0x%x\n",&u1.s);
    printf("0x%x\n",&u1.n);
    printf("0x%x\n",&u1.d);
    u1.n=1;
    printf("%d\n",u1.s[0]);
    printf("%lf\n",u1.d);
    unsigned char *p=(unsigned char *)&u1;
    printf("%d\n",*p);
    printf("%d\n",*(p+1));
    printf("%d\n",*(p+2));
    printf("%d\n",*(p+3));
    printf("%d\n",*(p+4));
    printf("%d\n",*(p+5));
    printf("%d\n",*(p+6));
    printf("%d\n",*(p+7));
    return 0;
}

结果:

16
8
0x22341fb0
0x22341fb0
0x22341fb0
0x22341fb0
1
1
0
0
0
0
0
0
0

浮点数值的存储

浮点数:R=M*2^e > R:real M:尾数 e:阶码

float: x xxxxxxxx xxxxxxxxxxxxxxxxxx [符号位] [阶码8,e=E-127] [尾数23]

float的二进制转换计算

例子
float 125.5的32二进制码为(小数部分转为二进制,将小数部分不断乘2,取整):
如:0.52 = 1,取整0.100000
则125.5的二进制数为:111 1101.1,科学计数表示为1.111101
2^6.
向左移6,e为正数,e = 6,E = e +127,E 二进制为10000101. 而1.111101去除1后为 111101,后面补0,共23bit。形成阶码。
则125.5的32二进制如:
> 0 10000101 11101 000000000000000000000

例子2:int转float
> int a = 3490593; > float b = (float)a;

那么在内存中a和b究竟存放的是什么值呢?

将a展开为二进制,其值为0000 0000 0011 0101 0100 0011 0010 0001,其十六进制即为0x00354321。 因为要转化为float型,所以首先要对上述二进制的表示形式改变为 M * 2^E 的形式.由于该数明显大于1,所以按照IEEE的标准,其浮点形势必然为规格化值。因此 ,转化后的形式为 > a = 1.101010100001100100001 * 2^21
根据 规格化值的定义,M = 1 + f. 所以f = 0.101010100001100100001.因为float型变量的小数域一共23位。所以b的最后23位可以得出,其值为10101010000110010000100
下面再演绎指数域的值:因为a的指数表示法中,指数e = 21。根据公式2,E = e + (2^7 -1) = 148.所以可以得出b的指数域的二进制表示为:10010100。在加上原数为正,所以符号位s=0。

所以,可以得出b的二进制表示为0 10010100 10101010000110010000100。转化为十六位进制则是0x4A550C84。换句话说,它存储在内存中的值是与a是完全不同的。但是其间还是有关联性的——a的首位为1的数值位后的二进制表示是与b的小数域完全相同的。

很快,问题就出现了。int型的有效位数是31,而float型小数域的有效位只有23位,也就是说如果上面的a的二进制的有效位超过了24位,那么float型的小数域的精度就不够了。因此必须进行舍入。比如:如果上面的a的二进制为0000 0001 1111 0101 0100 0011 0010 0001。这时b的小数域必须有24位才够,但是,这显然是不现实的,因此必须舍入到23位,舍入的原则是:所得结果的最低有效位为0。因此这个a在转换到float时,其精度就会丢失,因为该float的最后23位变成了11110101010000110010000——这显然是与原值不符的。

实际上,C语言中对于double型在32位机器上的小数域有52位,对于int型的31位有效位是绰绰有余了。这就是为什么大部分C语言教材上鼓励读者在执行强制类型转换时将int型转换成double。同时,这可能也是为什么int型能够直接隐式转换到double型的缘故。