嵌入式学习日志(二)

2   学习C语言的数据类型、运算符与表达式

2.1   C语言的数据类型

        C语言提供了以上一些数据类型,在程序中对用到的所有数据都必须指定其数据类型。数据有常量与变量之分,它们分别属于以上这些类型。例如整型数据包括整型常量和整型变量。

2.2   常量与变量

2.2.1   常量

        1.   常量在程序运行过程中,其值不能被改变的量称为常量。(常量不能被取地址)

        2.   宏定义指令(# define):是一个预处理指令,用于定义宏。例如," # define PI 3.1415926 " ,这里将 " PI " 定义为一个宏,在编译预处理阶段,代码中所有出现  PI  的地方都会被替换成常量  3.1415926  。

2.2.2   变量

        1.   变量:在程序运行过程中,其值可以发生改变的量称为变量(是系统在内存空间RAM当中分配的一段空间,空间的值可以伴随程序的运行动态的做出修改)。在C语言中,要求对所有用到的变量作强制定义,也就是 " 先定义,后使用 ” 。(变量可以寻址,取地址来获得变量在内存中的位置)

        2.   字节(Byte)是用来描述计算机存储的最小单位1 Byte = 8 bit 

        3  变量在内存当中的位置叫内存地址

#include<stdio.h>
int main (void)
{
    int i ;
    printf("%p\n",&i);
    return 0;
}

        在C语言中, printf("%p\n", &i);  这行代码的作用是打印出变量  i  在内存中的地址。具体解释如下:
(1) &i : &  是取地址运算符, &i  表示获取变量  i  的内存地址。
(2)  %p :是  printf  函数的一个格式控制符,专门用于以十六进制的形式输出指针(地址)值。
(3) \n :是换行符,使输出结果在新的一行显示。

        4.   标识符(identifier):对变量、符号常量、函数、数组、类型等数据对象命名的有效字符序列。

规则:(1)只能由字母、数字和下划线3种字符组成;

           (2)第一个字符必须为字母或下划线,不能为数字;

           (3)不能是关键字,下表为C语言中的关键字:

C语言中的关键字
autobreakcasecharconstcontinuedefaultdo
doubleelseenumexternfloatforgotoif
intlongregisterreturnshortsignedsizeofstatic
structswitchtypedefunionunsignedvoidvolatilewhile

           (4)大小写敏感;

           (5)尽量不要使用二类字例如 " include " " define " 等。

(注意:在gcc编译器中,允许将 " $ "  作为标识符的一部分来使用)

2.3   整型数据

2.3.1   整型常量的表示方法

        1.   整型常量即整常数。在C语言中,整常数可用以下3种形式表示。
        (1)十进制整数(DEC):如123、-456、4。(范围:0——9)
        (2)八进制整数(OCT):以0开头的数是八进制数。(范围:0——7)
        (3)十六进制整数(HEX):以0x开头的数是十六进制数。(范围:0——F)

        基于计算机的硬件是基于二进制逻辑设计的,其内部的存储单元和运算单元都是以二进制位(0和1)来表示和处理数据的。所以,无论何种数据类型,包括整型常量,在计算机中最终都要以二进制形式存储和运算。

        1.   十进制转二进制
        (1)整数部分采用除 2 取余法,将十进制数除以 2,取余数,然后将商继续除以 2,直到商为 0。将每次得到的余数从下往上排列,就是对应的二进制整数部分。(辗转相除法)

        (2)小数部分采用乘 2 取整法,将十进制小数乘以 2,取所得结果的整数部分,然后将小数部分继续乘以 2,重复这个过程,直到小数部分为 0 或者达到所需的精度为止。将每次得到的整数部分按顺序排列,就是对应的二进制小数部分。(辗转相乘法)
        例如,将十进制数 10 .625 转换为二进制:

整数部分十进制转二进制

10 / 2 = 5 …… 0
5 / 2 = 2 …… 1
2 / 2 = 1 …… 0
1 / 2 = 0 …… 1

小数部分十进制转二进制

0.625×2 = 1.25,整数部分是1,此时小数部分为0.25;
0.25×2 = 0.5,整数部分是0,此时小数部分为0.5;
0.5×2 = 1.0,整数部分是1,此时小数部分为0 ,转换结束。

        整数部分从下往上取余数得到二进制数为 1010,小数部分0.625转换为二进制为 0.101 ,综上: 10 .625 转换为二进制为 1010.101 。

        2.   八进制转二进制 

        因为 1 位八进制数对应 3 位二进制数,所以将八进制数的每一位转换为对应的 3 位二进制数即可。例如,将八进制数 13 转换为二进制:
        八进制的 1 转换为二进制是 001
        八进制的 3 转换为二进制是 011
        所以八进制数 13 转换为二进制是 001011,去掉前面的 0 为 1011。

八进制数01234567
二进制数000001010011100101110111

        3.   十六进制转二进制

        由于 1 位十六进制数对应 4 位二进制数,把十六进制数的每一位转换为对应的 4 位二进制数就行。例如,将十六进制数 1A 转换为二进制:
        十六进制的 1 转换为二进制是 0001
        十六进制的 A(A 对应十进制的 10)转换为二进制是 1010
        所以十六进制数 1A 转换为二进制是 00011010,去掉前面的 0 为 11010 。

十六进制数01234567
二进制数00000001001000110100010101100111
十六进制数89ABCDEF
二进制数10001001101010111100110111101111

2.3.2   整型变量

        1.   整型数据在内存中的存放形式

数据在内存中是以二进制形式存放的。

如果定义了一个整型变量i:

int i;            /*定义为整型变量*/
i = 10;            /*给i赋以整数10*/

        十进制数10的二进制形式为 1010 。int占4个字节,则该数据存放为 " 0000 0000 0000 0000 0000 0000 0000 1010 " 。实际上,数值是以补码(complement)表示的。

(1)正整数的补码和该数的原码(即该数的二进制形式)相同。

(2)负整数的补码是将该数的绝对值的二进制形式,按位取反再加一(取绝对值,按位取反再加一)。

(3)在存放整数的存储单元中,最高位是符号位,该位为0,表示数值为正;该位为1则表示数值为负。

(4)负整数补码到原码的转换:补码减一再按位取反。

例:求 -10 的补码方法:

①取-10 的绝对值 10 ;

②10的绝对值的二进制形式为 1010 ;

③对1010 取反得 1111 1111 1111 1111 1111 1111 1111 0101 (一个整数占32 位);

④再加1得            1111 1111 1111 1111 1111 1111 1111 0110 。

        2.   整型变量的分类(共8类)

(1)整型变量的分类(字节是用来描述计算机存储的最小单位,1 Byte = 8 bit

序号名称( " [ ] " 中的内容可有可无)

字节数

(Byte)

比特(位)数

(bit)

取值范围
1有符号短整型   [ signed ] short [ int ]216(-2^(15)) ~ (2^(15)-1)
2无符号短整型   unsigned short [ int ]2160 ~ (2^(16)-1)
3有符号整型   [ signed ] int432(-2^(31)) ~ (2^(31)-1)
4无符号整型   unsigned int4320 ~ (2^(32)-1)
5有符号长整型   [ signed ] long [ int ]864(-2^(63)) ~ (2^(63)-1)
6无符号长整型   unsigned long [ int ]8640 ~ (2^(64)-1)
7有符号长长整型   [ signed ] long long [ int ]864(-2^(63)) ~ (2^(63)-1)
8无符号长长整型   unsigned long long [ int ]8640 ~ (2^(64)-1)

        对于有符号整型,由于最高位为符号位,所以取值范围为 -2^31 ~ 2^31-1 ,而不是 -2^32 ~ 2^32-1 。

        C语言没有具体规定以上各类数据所占内存的字节数,只要求long型数据长度不短于int型,short 型不长于int型。具体如何实现,由各计算机系统自行决定。" sizeof( ) " 为长度运算符。

        ①  " %d " 用于输出有符号十进制整数,包括正整数、负整数和零,对应的变量类型通常是 int 、short 、 long 等有符号整型。

        ② " %u " 用于输出无符号十进制整数,只能表示非负整数,对应的变量类型通常是 unsigned int 、 unsigned short 、 unsigned long 等无符号整型。

        3.   整型数据的溢出

(1)对于有符号数

         一个有符号短整型变量只能容纳 -32768 ~ 32767 范围内的数,无法表示大于 32767 或小于 -32768 的数。遇到此情况就发生“溢出”。但运行时并不报错。

(2)对于无符号数

 

        一个无符号短整型变量只能容纳 0 ~ 65535 范围内的数,无法表示大于 65535 或小于 0 的数。遇到此情况就发生“溢出”。但运行时并不报错。

2.4   浮点型数据

2.4.1   浮点型常量的表示方法

        1.   十进制小数形式:它由数字和小数点组成(注意必须有小数点)。0.123、123. 、123.0、0.0都是十进制小数形式。

        2.   指数形式:如123e3或123E3都代表123X10^3。字母e(或E)之前必须有数字(正负都可),且e后面的指数必须为整数。(浮点数就是平常所说的实数)

2.4.2   浮点型变量

        1.   浮点型数据在内存中的存放形式

        浮点型数据与整型数据的存储方式不同,浮点型数据是按照 " 符号位、阶码、尾数 " 形式存储的。

        (1)符号位:用来表示该浮点数的正负,通常0表示正数,1表示负数

        (2)阶码:对已知数的绝对值转换为二进制数,用科学计数法表示,将2的幂次项+偏移量表示为阶码。

        (3)尾数:采用原码表示,存储的尾数是小数点后的数,后面补零。

例如:-6.25 ,为负数,所以符号位为1。 -6.25 的绝对值转换为二进制数: 110.01 ,用科学计数法表示为 1.1001 * 2^2 , 对应的阶码为2的幂次项 " 2 " + 偏移量(对于单精度浮点数,偏移量为127;对于双精度浮点数,偏移量为1023)。尾数为小数点后的数 "1001 "后面补零。

        2.   浮点型变量的分类

序号名称

字节数

(Byte)

比特(位)数

(bit)

取值范围
1单精度浮点数   float432(-3.4*10^(-38)) ~ (3.4*10^(38))
2双精度浮点数   double864(-1.7*10^(-308))~ (1.7*10^(308))
3长双精度浮点数   long double16128(-1.2*10^(-4932))~ (1.2*10^(4932))

        (1)float型共占32位bit,其中符号位占1位bit,阶码占8位bit,尾数占23位bit 。

        (2)double型共占64位bit,其中符号位占1位bit,阶码占11位bit,尾数占52位bit 。

        " %f "用于以小数形式输出单精度或双精度浮点数。默认输出时,会保留小数点后6位 。

对于下面的程序

 

打印结果为 " No " 的原因:
        
数据类型差异:在C语言中,字面值 0.9 默认是双精度( double )类型 。而代码中 f 是单精度( float )类型。当进行 f == 0.9 比较时, f 会被隐式转换为双精度类型再比较,但由于此时是尾数位补29个0,与本身双精度的 0.9 在计算机种存储的数值不同(阶码,尾数都不同),所以导致打印结果为 " No " 。

解决方案(要使比较工作正常):

①使用相同的数据类型:
    if(f == 0.9f)  // 使用f后缀表示float类型    

②或者将f声明为double:
   double f = 0.9;
   if(f == 0.9)

③更安全的做法是比较浮点数的差值是否在某个很小的范围内:
   if(fabs(f - 0.9) < 0.000001)

总结:在做比较运算时,左右两边数据类型一定要相符。

2.5   字符型数据

2.5.1   字符常量

        C语言的字符常量是用单撇号括起来的一个字符。如'a'、'x'、'D'、'?'、'$'等都是字符常量。注意,'a'和'A'是不同的字符常量。除了以上形式的字符常量外,C还允许用一种特殊形式的字符常量,就是以一个字符“\”开头的字符序列,称为转义字符。

转义字符及其作用
字符形式含义ASCII代码
\n换行,将当前位置移到下一行开头10
\t水平制表,跳到下一个 Tab 位置(每一列宽度相同8bit)9

\b

退格:将光标往前挪一格,并不删掉8
\r

回车:把光标从本行当前位置移到本行最左边

(区别换行:将光标从上一行挪到下一行)

13
\f换页12
\\代表一个反斜杠字符 " \ "92
\'代表一个单引号(撇号)字符39
\"代表一个双引号字符34
\ddd1到3位八进制数所代表的字符
\xhh1到2位十六进制数所代表的字符

2.5.2   字符变量(与整型兼容)

名称

字节数(Byte)

比特(位)数(bit)

取值范围
char18-128 ~ 127
unsigned char180 ~ 255

2.5.3   字符数据在内存中的存储形式及其使用方法

        将一个字符常量放到一个字符变量中,实际上并不是把该字符本身放到内存单元中去,而是将该字符的相应的ASCII代码放到存储单元中。

常用字符ASCII代码
0~948~57
A~Z65~90
a~z97~122

大写字母转小写字母:' A ' + ' 32 '

小写字母转大写字母:' a '  - ' 32 '

2.5.4   字符串常量

        字符串常量是一对双撇号括起来的字符序列。不能把一个字符串常量赋给一个字符变量。C规定以字符'\0'作为字符串结束标志。例如字符串" CHINA"实际上在内存中是:

CHINA\0

        注意,在写字符串时不必加'0',否则会画蛇添足。\0'字符是系统自动加上的。字符串"a"实际上包含2个字符:'a'和'0',因此,想把它赋给只能容纳一个字符的字符变量c显然是不行的。

2.6   变量初始化

1. 在定义变量的同时使变量初始化:int a = 10; /*对变量i的初始化*/(变量初始化效率更高)

正确初始化方法:(1)int a,b,c = 10;

                             (2)int a = 10,b = 10,c = 10;

错误初始化方法:         int a = b = c = 10;

2.7   各类数值型数据间的混合运算

2.7.1   隐式转换  

        整型、浮点型、字符型数据间可以混合运算。 在进行运算时,不同类型的数据要先转换成同一类型,然后进行运算。转换的规则按上图所示。

        横向向左的箭头表示必定的转换,即 char 型,short 型 必定先转换为 int 型 ;float 型 必定先转换为 double 型 ,以提高运算精度。( 即使是两个 float 型 数据相加,也先都化成double型,然后再相加)。

        纵向的箭头表示当运算对象为不同类型时转换的方向,由低向高转换。  如 int 型 与 double 型 数据进行运算,先将 int 型 的数据转换成 double 型,然后在两个同类型( double 型 )数据间进行运算,结果为 double 型。

2.7.2   显式转换(强制类型转换)

        通过使用强制类型转换运算符来实现的,其一般形式为:" (类型名)(表达式) "。

 

        在这个示例中,将 float 型的变量 f 强制转换为 int 型并赋值给 i ,输出结果可以看到 f 的值保持不变,而 i 的值为 7 ,小数部分被舍去。因此强制类型转换可能会导致数据丢失或精度降低。

 

        由于 " printf " 默认保留小数点后六位,为避免小数点后超过六位导致的输出四舍五入,可以进行如上的强制类型转换保留前六位小数,舍去后面的小数部分。

2.8   算术运算符和算术表达式

2.8.1   基本的算术运算符

1.   +(加法运算符,或正值运算符,如3 + 5、+ 3);

2.   - (减法运算符,或负值运算符,如5 - 2、-3);

3.   *(乘法运算符,如3 * 5);

4.   / (除法运算符,如5 / 3);

5.   %(求余运算符,如7 % 4的值为3);

 注意:(1)%求余运算符只能用在整型或整型相兼容(字符型)的数据类型;

            (2)%求余运算符的最终符号由其左操作数决定;

            (3)%求余运算结果一定小于右操作数;

            (4)%求余运算右操作数不能为 0 。

基本算术运算符的优先级排序:  (  * 、 / 、 %  )   >   (  + 、 - )

2.8.2   自增、自减运算符

 1.   自增运算符为 ++ ,自减运算符为 -- 

对于一个已经定义的 i 变量:

        前置自增 ++i ,先将变量 i 的值在内存中直接加 1,然后再使用 i  增加之后的值参与后续运算。(++i 无临时空间,++i 和 i 用的是同一段空间)

        后置自增  i++  ,会先创建一个临时变量(匿名变量)来保存  i  原来的值,然后让  i  在内存中的值增加 1,但是表达式  i++  的值是使用那个临时变量中保存的  i  原来的值,而不是  i  增加 1 之后的值。(i++ 有临时空间 ,i++ 的空间值未加,而 i 的值加了)

( ++i 效率高于 i++ 。在同一个表达式下,不得对同一个变量进行多次反复的自增自减运算,由于编译器不同,会导致结果不确定)

2.   (1)所有的临时变量都为右值;

      (2)所有的常量都为右值;

      (3)对于 const int i = 10 ;   关键字const修饰的 i 为只读变量(只能被初始化,不能被赋值)只读变量为左值。( const int i = 10 ;  与  int  const  i = 10 ;  是一样的)

判断左 / 右值的依据:是否能被取地址,左值能被取地址,右值不能被取地址。

左值:locatiable value     ;右值:readable value 。

2.9   赋值运算符

2.9.1   赋值运算符 

        =  、 += 、  -= 、  *= 、  /= 、  %= 、  >>= 、  <<= 、  &= 、   ^= 、  |=    优先级为14级,结合方向自右至左  ," a += 5 " 等价于 " a = a + 5 " 等复合赋值运算符原理类似。

2.9.2   类型转换

        1. 低精度到高精度:从低精度类型转换为高精度类型,数据能完整保留,不会丢失信息 ,如 int 转 double  。
        2. 高精度到低精度:高精度类型转换为低精度类型,会截断小数部分,导致信息丢失 ,如 double 转 int  。
        3. 大范围整数到小范围整数:大范围整数类型转换为小范围整数类型,高位二进制位截断,信息丢失 ,如 long 转 short  。
        4. 小范围整数到大范围整数:有符号数高位按符号位扩展,无符号数高位补零扩展 ,如 short (有符号/无符号)转 int  。
        5. 有符号数到无符号数:二进制位不变,解读方式改变,数值含义可能变化 ,如 char (有符号)转 char (无符号) 。

2.10   逗号运算符和逗号表达式

        优先级为15级(最低),结合方向自左至右,也称为顺序求值运算符,用在循环问题中。

int t;
t = (1 + 5 , 6 - 2 , 3 * 7);
printf("%d\n",t);

        如上述代码,最终结果由于逗号运算符顺序求值,结果为 3 * 7 = 21 。若没有括号将表达式括起来,即 t = 1 + 5 , 6 - 2 , 3 * 7;那么输出的结果将为 1 + 5 = 6 。

        另,在函数调用过程当中,出现的逗号与在一般表达式当中的逗号不一样,例printf("%d\n",t);这里的逗号用作实参与实参之间的分隔符。

printf("%d\n",1 + 5 , 6 - 2 , 3 * 7);

        对于上述代码,运行会出现问题,因为此时逗号相当于四个实参间的分隔符;要解决问题就要引入括号,即

printf("%d\n",(1 + 5 , 6 - 2 , 3 * 7));

才能运行出值为21的结果,括号里的逗号才为逗号运算符。

学习总结: 

        在C语言中,%d 、 %u 、 %c 、 %f 、 %p  是格式说明符,用于 printf  等输出函数(部分也用于 scanf  等输入函数 )来指定数据的输入输出格式,具体用法如下:

1.   %d :用于输出有符号十进制整数 。

2.   %u :用于输出无符号十进制整数。无符号数不存在负数形式,其表示范围是从 0 开始的正整数 。

3.   %c :用于输出单个字符。

4.   %f :用于输出浮点数,默认保留6位小数。(在printf函数中单 / 双精度都可用%f ,但在scanf里对双精度必须要用%lf)

5.   %p :用于输出指针的值,也就是内存地址,通常以十六进制形式显示。

        大范围整数类型转换为小范围整数类型,高位二进制位截断,信息丢失 ,如 long 转 short  。
        小范围整数类型转换为大范围整数:有符号数高位按符号位扩展,无符号数高位补零扩展 ,如 short (有符号/无符号)转 int  。

         各类数值型数据间的混合运算,char 型,short 型 必定先转换为 int 型 ;float 型 必定先转换为 double 型 。

整型数据溢出:

        原理:C 语言中不同整型数据类型有固定的存储大小和表示范围,当运算结果超出该类型能表示的最大值或最小值时,就会发生溢出。整型数据以二进制补码形式存储,有符号整型的最高位是符号位,0 表示正数,1 表示负数。当进行运算导致二进制表示超出范围时,会产生溢出。

        以int类型为例,取值范围是 (-2^(31)) 到 (2^(31)-1) ,当执行以下代码时:

#include <stdio.h>
int main() 
{
    int a = 2147483647;  /* 最大的int值 */
    int b = a + 1;
    printf("b的值为: %d\n", b);
    return 0;
}

        运行结果中b的值会变为 -2147483648 ,这是因为 2147483647 加 1 后,二进制表示发生溢出,最高位变为 1,被解释为负数,成为 int 类型能表示的最小值。

        影响:溢出后,程序不会报错,但结果是错误的,可能导致程序逻辑出现问题,如循环条件判断错误等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值