深入理解C语言-02-数据编码

信息系统建模中,第一步是信息的编码,也就是说,信息如何在计算机中存储。

为了硬件设计的简单,通常使用芯片均采用二进制。并且,由于科技的局限性,数据的长度也是有限的。

比如,现在大多数电脑的数据总线是32位/或者64位。以32位系统为例,能编码的集合大小为 2的32次方,也就是4294967296。


显然这是一个有限集合。而现实中的模拟信息通常是无限集合。

这就涉及到信息的编码,即建立一个映射函数: f(信息)=计算机中的信息编码。


信息的编码设计涉及到数据的大小选择,实际项目中一般是考虑当前需求和后期的扩展选择一个折中值。


对于C语言来说,主要考虑以下3个方面:

数据类型的大小

数据的字节序

数据的对齐


数据类型大小的差异是C语言可移植性方面的大问题。


为此,在新系统使用前,需要参考芯片手册(DataSheet)或写如下代码进行测试。

printf("char[%d] short[%d] int[%d] long[%d] long long[%d] float[%d] double[%d] \n",
sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long), 
sizeof(float), sizeof(double));


在32位系统,一般返回以下结果:

char[1] short[2] int[4] long[4] long long[8] float[4] double[8]


数据的字节序

通常,x86架构采用的是小端表示(Little-Endian), MIPS架构采用的是大端表示(BigEndian)。

比如0x12345678 在x86中是 按照 0x78 0x56 0x34 0x12 的顺序依次存储,

而在MIPS架构中则通常是按照 0x12 0x34 0x56 0x78   的顺序依次存储。

可以写以下代码来测试大小端:

int checkEndian()
{
int x = 0x12345678;
if (*(char*)(&x) == 0x78) {
printf("Little Endian\n");
return 0;
} else {
printf("Big-Endian");
return 1;
}
}


数据的对齐:

为什么要考虑对齐? 一方面是为了性能考虑,另一方面则是芯片设计上的限制。

比如公司使用的MIPS芯片要求数据地址必须4字节对齐,否则就会总线错误(Bus Error)


当然,按照时间空间的矛盾性,对齐必然会导致内存的浪费。这样在E2ROM使用上来说,也就意味着硬件成本的上升。


如何节约? 一种办法是牺牲时间,进行数据压缩。还有一种更方便的办法就是自己控制对齐。

当然,最好的设计就是从整体上考虑对齐,合理安排数据的位置。


控制对齐的方式与编译器有关系,通常:

gcc采用

__attribute__ ((aligned (n))

vc采用:

#pragma pack(n)


可以写如下代码进行测试:

VC:

typedef struct  stTest1 
{
char ch;
int x;
short y;
} Test1;


#pragma pack(1)
typedef struct  stTest2
{
char ch;
int x;
short y;
} Test2;
#pragma pack(4)


gcc:

typedef struct  stTest1 
{
char ch;
int x;
short y;
} Test1;


typedef struct  stTest2
{
char ch;
int x;
short y;
} Test2 __attribute__ ((aligned (1));


Log代码:

printf("sizeof(Test1)= %d, sizeof(Test2) = %d \n", sizeof(Test1), sizeof(Test2));
printf("__alignof(Test1)= %d, __alignof(Test2) = %d \n", __alignof(Test1), __alignof(Test2));


输出:

sizeof(Test1)= 12, sizeof(Test2) = 7
alignof(Test1)= 4, __alignof(Test2) = 1


接下来就是数据的表示。

通常我们有以下方式:

1> 利用bit位,设计每一个数据的存储位置与大小。

比如一副扑克牌,有4种花色,每一种花色有13种取值,这样我们便可以按如下方式编码:

1个字节有8个Bit位, 0-3 4个Bit 表示A-K, 4-5 2个Bit表示花色,大小王特殊编码。

8个Bit    7    6     5       4       3      2      1  0

                                                |                    |

                                                —— ————     ->  0001 -> A,  0001-> 2, ..., 1101 -> K 

                             |         |

                             ——     ->  00 -> 红桃 01-> 黑桃  02-> 黑梅 03 -> 红方

大王:       000000

小王:       111111


2> 利用C语言提供的基本数据类型(char, short, int, unsign int, float, double),并配合数组与结构体来构建复杂数据结构。

    对于,非线性结构,还需要指针来配合。

在使用基本类型前,按照C语言的设计哲学(谁使用谁负责理念),必须搞懂以下几点:

1> 各种格式的表示范围

2> 数据的二进制值是什么

3> 是否有精度丢失或数据溢出?如何判断?


2.1 表示范围

以无符号整数为例,在32位系统下:

unsigned char     8位    表示范围   0~255  

unsigned short    16位    表示范围   0~65535

unsigned long   32位    表示范围   0~4294967295

unsigned long long   64位    表示范围   0~18446744073709551615

 

这些值并不需要记忆精确值,只需要大致了解级数就可以了。

具体值我们可以通过下面代码来了解:

#include<limits.h>

int testLimit()
{
printf("min of char: %d \n", SCHAR_MIN);
printf("max of char: %d \n", SCHAR_MAX);
printf("min of short: %d \n", SHRT_MIN);
printf("max of short: %d \n", SHRT_MAX);
printf("min of int: %d \n", INT_MIN);
printf("max of int: %d \n", INT_MAX);
printf("min of long: %d \n", LONG_MIN);
printf("max of long: %d \n", LONG_MAX);
printf("min of long long: %llu \n", LLONG_MIN);
printf("max of long  long: %llu \n", LLONG_MAX);


printf("max of unsigned char: %d \n", UCHAR_MAX);
printf("max of unsigned short: %d \n", USHRT_MAX);
printf("max of unsigned int: %u \n", UINT_MAX);
printf("max of unsigned long: %u \n", ULONG_MAX);
printf("max of unsigned long long: %llu \n", ULLONG_MAX);
return 0;
}

在32位系统上输出:

min of char: -128
max of char: 127
min of short: -32768
max of short: 32767
min of int: -2147483648
max of int: 2147483647
min of long: -2147483648
max of long: 2147483647
min of long long: 9223372036854775808
max of long long: 9223372036854775807
max of unsigned char: 255
max of unsigned short: 65535
max of unsigned int: 4294967295
max of unsigned long: 4294967295
max of unsigned long long: 18446744073709551615

关于浮点数请参考float.h头文件。以下翻译来自百度文库:

double:


 DBL_DIG double小数点后面精确的位数 
 DBL_EPSILON  最小的尾数 (1.0+DBL_EPSILON != 1.0)

 DBL_MANT_DIG 尾数中的位数 
 DBL_MAX 最大值  

 DBL_MAX_10_EXP 最大10进制指数 
 DBL_MAX_EXP 最大2进制指数 

 DBL_MIN 最小值  

 DBL_MIN_10_EXP 最小10进制指数
 DBL_MIN_EXP 最小2进制指数 


float :

  FLT_DIG float小数点后面精确的位数
  FLT_EPSILON 最小的尾数 (1.0+FLT_EPSILON != 1.0)

  FLT_MANT_DLG 尾数中的位数 
  FLT_MAX 最大值  

  FLT_MAX_10_EXP 最大10进制指数 

FLT_MAX_EXP 最大2进制指数 
FLT_MIN 最小值  

FLT_MIN_10_EXP 最小10进制指数 
FLT_MIN_EXP 最小2进制指数
 
FLT_RADIX 进制基数 FLT_ROUNDS 加法舍入 

long double:

  LDBL_DIG long double小数点后面精确的位数 
  LDBL_EPSILON 最小的尾数 (1.0+LDBL_EPSILON != 1.0)
  LDBL_MANT_DLG 尾数中的位数 

 LDBL_MAX 最大值 
 LDBL_MAX_10_EXP 最大10进制指数 

 LDBL_MAX_EXP 最大2进制指数 

 LDBL_MIN 最小值  

 LDBL_MIN_10_EXP 最小10进制指数 

 LDBL_MIN_EXP 最小2进制指数  

你可能感兴趣的:(C语言)