欢迎光临散文网 会员登陆 & 注册

计算机程序基础教程(06):C语言 - 基础数据类型

2023-02-26 00:47 作者:阿狸喜羊羊  | 我要投稿


【基础数据类型】


C语言将基础数据分为多种类型,使用不同的关键词表示,可以分为以下几大类:整数型、浮点型、字符型、布尔型。


 ● 整数型


整数类型又分为多种长度,每种长度又分为有符号数和无符号数,定义关键词如下:


unsigned char,无符号数,长度1字节,表示范围0-255。

unsigned short,无符号数,长度2字节,表示范围0-65535。

unsigned int,无符号数,长度4字节,表示范围0-4294967295。

unsigned long long,无符号数,长度8字节,表示范围0-18446744073709551615。


signed char,有符号数,长度1字节,表示范围 -127 - 128。

signed short,有符号数,长度2字节,表示范围 -32768 - 32767。

signed int,有符号数,长度4字节,表示范围 -2147483648 - 2147483647。

signed long long,有符号数,长度8字节,表示范围 -9223372036854775808 - 9223372036854775807。



 ● 浮点型


float,单精度浮点型,长度4字节。

double,双精度浮点型,长度8字节。



 ● 字符型


字符型其实就是char数据,用于存储字符编码的char数据称为字符型数据,为char类型数据赋值时可以使用一个英文字符赋值,编译器会转换为此字符的字符编码。



ASCII字符集如下:


 ● 布尔型


布尔型也是一个char数据,但是它只用来存储0和1,用于表示两种逻辑运算的结果,1表示对(true),0时表示错(false)。


在VC编译器中使用bool定义,使用true、false赋值,示例:bool a = true;

在GCC编译器中使用_Bool定义,使用0和1赋值,示例:_Bool a = 1;


若将布尔数据赋值为大于1的数据,则统一表示对(true),将其当做1处理。


 ● 常量与变量


变量是可以修改的数据,常量是不能修改的数据,常量需要在定义时赋值,并且之后不能再修改它的值,定义数据时默认是变量,添加const关键词指定为常量。





【运算符】


 ● 数学运算符


+,加法

-,减法

*,乘法

/,除法

%,取余

++,自加1

--,自减1


C语言除法运算不保留余数,对于计算结果为小数的情况需要进行取整操作,C语言使用向0取整的规则,3.5取整为3,-3.5取整为-3,除法结果直接丢弃余数即为向0取整的结果。



 ● 数学关系运算符


用于判断数据之间的大于、小于、等于关系,运算结果为一个布尔值,关系正确返回1,错误返回0。


>,大于

<,小于

==,等于

!=,不等于

>=,大于或者等于

<=,小于或者等于


 ● 逻辑关系运算符


用于对布尔数据进行运算,运算结果是一个布尔值。


&&,与运算,两个布尔数据都为1则结果为1,否则为0。

||,或运算,两个布尔数据有一个为1则结果为1,否则为0。

!,非运算,将一个布尔数据取反值,1变0,0变1。



 ● 移位运算符


将一个数据进行算数移位操作。


<<,算数左移

>>,算数右移


使用算数右移代替除以2的操作应该慎用,当除法运算无法整除时会涉及到向0取整操作,而使用算数右移会直接丢弃低位,导致右移结果与除法结果可能会不同。


 ● 按位逻辑运算符


将一个数据按照二进制位进行逻辑运算,得出一个新的数据。


&,按位与运算

|,按位或运算

^,按位异或运算,判断两个数字是否不同,若不同则运算结果此位为1,否则为0。

~,按位取反运算,将一个数据的每个二进制位取反值得出运算结果。



 ● 赋值运算符


=,赋值

+=,加法运算并赋值,A += B,等同于 A = A+B

-=,减法运算并赋值

*=,乘法运算并赋值

/=,除法运算并赋值

%=,取余运算并赋值

<<=,左移运算并赋值

>>=,右移运算并赋值

&=,按位与运算并赋值

|=,按位或运算并赋值

^=,按位异或运算并赋值




【数学运算优化】


当进行数学运算时,编译器在特定情况下会进行优化,比如两个常量之间的运算,编译器会在编译期间计算出结果,无需在程序执行期间计算,若变量与常量之间进行乘法、除法运算,编译器会使用执行速度更快的指令代替乘法或除法,得出相同结果。


其中除法指令最为耗时,优化也最为复杂,若除数为2的乘方,则转换为右移,之后进行向0取整规则调整,若除数不是2的乘方,则编译器转换为乘法,转换原理与减法转加法类似,也是先扩大再缩小,这里的缩小使用移位的方式实现。


使用10进制说明转换原理:


115 / 5 = 23

可以转换为

115 * 2 = 230,230右移1次等于23


因为除以5与乘以2的结果相差10倍,所以除以5转换为乘以2之后只需要再除以10即可,而除以10可以无需计算,右移1次即可。


 ● 有符号变量除以9


编译器会将除法转换为如下指令:

除法结果无法整除时需要进行向0取整,上述运算后,若被除数为负数,则计算结果需要额外加1才能满足向0取整的规则,末尾的两条指令用于实现加1运算。

首先将eax中的被除数算数右移31位,之后计算结果减eax,若被除数为负,则减-1,等同于加1,若被除数为正,则减0。


 ● 有符号变量除以7


编译器会将除法转换为如下指令:


上面的优化方案中,被除数乘以一个数之后又执行了一个加法,之后右移2次得出除法结果,原因是编译器进行除以7的优化时,无法计算出误差较小、长度又不超过32位的乘数,所以编译器使用将被除数缩小再右移的方式进行优化。


右移2位等于除以4,这里将除以7转换为除以4,两者若要相等的话,被除数必须首先减去自身的3/7,这样才能保证其1/4等于原值的1/7。

而计算被除数的3/7却又产生了另一个除法,实际上这个除法也会被编译器优化为乘法,首先 被除数 × 92492493h,之后使用rdx,等于乘法结果右移32位,这个结果不算符号位的话,就是被除数的3/7。

之后被除数与rdx相加,0x92492493转换为二进制是一个负数的补码,若被除数为负,则乘法结果为正,被除数会加一个正数,等于被除数减自身3/7,若被除数为正,则乘法结果为负,被除数会加一个负数,等于被除数减自身3/7。

最后右移2位,得出除以7的结果。


 ● 无符号变量除以7


编译器会将除法转换为如下指令:


这里除数为7,也会有之前的问题,就是编译器无法计算出误差较小、长度又不超过32位的乘数。

其实第三条乘法指令之后,使用存储高32位结果的edx就是除法结果,只不过这个结果有时候会不太精确,可能会比正确值大1,使用之后的4条指令进行调整将会消除误差。


下面解释后4条指令的作用:


计算机程序基础教程(06):C语言 - 基础数据类型的评论 (共 条)

分享到微博请遵守国家法律