710 likes | 865 Views
第二章 数据类型及其运算. 内存的逻辑结构. 内存以字节作为基本的存储单元。 以字节为单位对每个存储单元进行统一编号。这些编号我们形象地称为内存地址。 一个数据占用 1 个或多个字节的存储空间。 C 语言中通过将数据进行分类来实现不同类型的数据占用不同大小的存储空间。. 2.1 变量. 对于数学中定义的函数 f(x) = 3x + 1 , x 称为自变量,其值可以改变。而 3 和 1 则是固定的,其值不能改变。 程序中的数据也有常量和变量之分。值可以改变的数据称为变量。 在函数中,自变量通过指定不同的值而改变。程序设计中的变量值的改变则与内存的存放密切相关。.
E N D
内存的逻辑结构 • 内存以字节作为基本的存储单元。 • 以字节为单位对每个存储单元进行统一编号。这些编号我们形象地称为内存地址。 • 一个数据占用1个或多个字节的存储空间。 • C语言中通过将数据进行分类来实现不同类型的数据占用不同大小的存储空间。
2.1 变量 • 对于数学中定义的函数 f(x) = 3x + 1,x称为自变量,其值可以改变。而3和1则是固定的,其值不能改变。 • 程序中的数据也有常量和变量之分。值可以改变的数据称为变量。 • 在函数中,自变量通过指定不同的值而改变。程序设计中的变量值的改变则与内存的存放密切相关。
在程序设计中,变量表示内存中一块固定的存储空间,该存储空间具体占几个字节由变量定义时指定的变量类型决定。变量的值其实就是变量所表示的存储空间中所存放的内容。由于存储空间中可以存放不同的值,因此变量的值可以改变。在程序设计中,变量表示内存中一块固定的存储空间,该存储空间具体占几个字节由变量定义时指定的变量类型决定。变量的值其实就是变量所表示的存储空间中所存放的内容。由于存储空间中可以存放不同的值,因此变量的值可以改变。 • 另外,在定义变量时,我们还要指定变量的名字。通过使用变量名,我们可以找到变量表示的存储空间,并访问其中存放的变量值。
变量的名必须是合法的标识符。 • 对变量、符号常量、函数、数组、类型等对象命名的有效字符序列称为标识符。简单来说,标识符就是一个名字。 • C语言中的标识符必须满足以下三个规定: • (1)只能由字母、数字、下划线三个三种字符组成; • (2)第一个字符必须是字母或下划线; • (3)不能与关键字(关键字也可以称为保留字、系统标识符)重名。
C语言规定:任何变量在使用之前必须进行定义。编译器将根据变量的定义在内存中为变量分配相应的存储空间。C语言规定:任何变量在使用之前必须进行定义。编译器将根据变量的定义在内存中为变量分配相应的存储空间。 • 变量定义的格式如下: • 类型标识符 变量名; int a , b , c;
2.1.1 整型变量 • 整型的基本类型标识符为int(integer的缩写)。 • 根据表示的数据范围的不同整型又分为 • 短整型(short int) • 基本整型(int) • 长整型(long int) • 整型数据的表示范围是由该类型数据所占的存储空间字节数所决定的。
三种整型类型所占的字节数由编译器决定。不同的编译器规定的字节数可能不同。例如:Visual C++6.0编译器规定short int类型占两个字节,int类型占四个字节,long int类型占四个字节。因此,在不同的编译环境中同一类型的变量所能存储的数据的范围可能不同。 • 根据不同的表示需求,三种整型又分别提供有无符号类型和有符号类型。无符号类型只能表示非负数,而有符号类型即可以表示正数也可以表示负数。
使用Visual C++6.0编译器时,六种类型的整型变量能存储的数据范围如表所示。 • 其中,类型标识符中,[ ]含义是其包含的内容可以省略,省略的前提是不会引起混淆。
short a; /* 定义一个短整型的变量a */ int b; /* 定义一个基本整型变量b */ long c; /* 定义一个有符号的长整型变量c */ unsigned short d; /* 定义一个无符号的短整型变量d */ unsigned e; /* 定义一个无符号的基本整型变量e */ unsigned long f; /* 定义一个无符号的长整型变量f */ • 另外,同类型的变量可以通过一个类型标识符定义。 int a ,b , c; /* 定义三个int类型的变量a、b、c */
2.1.2 实型变量 • 如果需要处理带有小数的数据,变量类型应定义为实型。实型数据又称作浮点型数据。 • 根据表示范围与精度的不同,浮点类型又分为单精度类型(float)、双精度类型(double)和长双精度类型(long double)。 • 浮点类型没有有符号数和无符号数之分,浮点类型变量既可以存放正数,又可以存放负数。
在实际应用中,具体选择哪种类型取决于对精度和数据范围的要求。当精度的要求不高时,float类型是一个合适的选择;当精度的要求较高时,可选择使用double类型或long double类型。double类型可以提供满足大多数程序所需要的精度,long double类型可以提供更高的精度,一般很少用到。
float a; /*定义一个单精度类型的变量a*/ double b; /*定义一个双精度类型的变量b*/ long double c;/*定义一个长双精度类型的变量c*/
2.1.3 字符变量 • 由于计算机是以二进制的形式进行存储、运算、识别和处理的。因此,像字母、数字、标点符号等各种字符也必须按特定的规则转换为二进制编码才能存入计算机。 • 字符编码实际上就是为每一个字符确定一个对应的整数值(以及它对应的二进制编码)。 • 为了在计算机中使用的方便,字符的编码都是从0开始,连续排列的。实际上,由于字符(包括拉丁字母)与整数值之间没有什么必然的联系,某一个字符究竟对应哪个整数完全可以人为地规定。
为了信息交换的统一性,人们已经制定了一些字符编码标准。其中以ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)字符编码标准使用的范围最广泛。 • 在ASCII编码中,每个字符的编码存储在一个字节(8位二进制位)中,规定字节的最高位为0,余下的7位给出字符的编码,这样就可以给出128个编码,用来表示128个不同的字符。
字符变量中存放的是字符的ASCII码值。例如,若某字符变量当前存放的数据为字符’a’,那么在内存中其实存放的是’a’的ASCII码值97。由于ASCII码需要存储在一个字节,因此所有的字符变量均占一个字节的存储空间。字符变量中存放的是字符的ASCII码值。例如,若某字符变量当前存放的数据为字符’a’,那么在内存中其实存放的是’a’的ASCII码值97。由于ASCII码需要存储在一个字节,因此所有的字符变量均占一个字节的存储空间。 • 字符变量的定义形式如下: char a; /*定义一个字符类型的变量a */
2.2 常量 • 在程序运行过程中,其值不可以改变的数据称为常量。 • 常量也有类型之分。 • 0、-1、3为整型常量; • 1.23、2.0为实型常量; • ’a’、’b’为字符常量。 • 常量一般通过字面形式即可辨别其类型,因此上述常量称为字面常量或直接常量。
符号常量:用标识符代表常量 • 定义格式:#define 符号常量常量 • 一般用大写字母 • 是宏定义预处理命令,不是C语句,后面没有分号
【例】 符号常量举例 #define PRICE 30 main() { int num,total; num=10; total=num*PRICE; printf("total=%d",total); }
2.1.1 整型常量 • 在C语言中,整型常量可以用以下三种形式表示: • (1)十进制形式。如123、-99等; • (2)八进制形式。为了与十进制形式进行区分,C语言规定所有的八进制常量均以0开头,如0123、027等; • (3)十六进制形式。为了方便区分,C语言规定所有的十六进制常量均以0x开头,如0x123、0xff等。 • 整型常量也有类型,当程序中使用整型常量时,如果它属于int类型的取值范围,编译器会将该常量作为基本整型数据来处理,否则作为长整型数据来处理。
2.2.2 浮点型常量 • 在C语言中,浮点型常量可以用以下两种形式表示: • (1)十进制小数形式。如1.03、.123、1.等(注意必须有小数点); • (2)指数形式。如1.123e3或1.123E3都代表1.123×103。需要注意的是,在该形式下,字符e或E前面必须有数字,其后的指数必须为整数(可以是负数)。 • 浮点型常量在内存中是以二进制指数形式进行存放。浮点型常量默认情况下为double类型。
【例】请分析以下数值哪些是C语言中的合法常量:-80、-080、-8e1.0、-80.0e【例】请分析以下数值哪些是C语言中的合法常量:-80、-080、-8e1.0、-80.0e • -80是一个十进制常量,而且值为负数,因此是合法的整型常量。 • 对于-080,根据其前缀为0我们可以得出其为八进制常量,但八进制中不可能出现数码“8”,因此该值不是合法的。 • -8e1.0与-80.0e属于实型常量的指数形式,但指数形式规定,e前面必须有数字,且e后面的数必须为整数,因此这两个数值也是不合法的。
2.2.3 字符常量 • C语言中的字符常量是用一对单引号括起来的一个字符,如’A’、’a’、’?’、’ ’等。 • 在C语言中,字符数据实际上是作为整数来处理的,也就是说字符常量在内存中是作为一个整数存放的,该整数就是字符对应的ASCII码值。 • 例如,在ASCII字符集中,字符’0’的值为48,它与整数0是完全不同的。 • 字符常量可以像其它整数一样参与算术运算。
字符转义序列 • 某些特殊字符(例如换行符)需要通过字符转义序列来表示。字符转义序列以字符’\’开头,后面跟上若干的字符,这样看起来是多个字符,但只表示一个字符。例如转义序列’\n’表示换行符。
其中’\000’表示一个八进制形式的ASCII码值所对应的字符。其中’\000’表示一个八进制形式的ASCII码值所对应的字符。 • 例如’\101’代表八进制形式的ASCII码值为101的字符’A’。’\12’或’\012’则可以表示换行符。字符常量’\0’表示ASCII码值为0的字符,也就是空字符。 • 同理,’ \xhh’表示十六进制形式的ASCII码值所对应的字符。 • 例如,’\x41’代表字符’A’。
h e l l o \0 2.2.4 字符串常量 • 字符串常量是用一对双引号括起来的0个或多个字符组成的字符序列。 • 例如 ”I am a string”“” • 双引号不是字符串的一部分,它只用于限定字符串。字符转义序列同样可以用在字符串中, • 例如 ”Hello world!\n” • 字符串常量中的字符在内存中依次存放。 例 字符串“hello”在内存中 \0 例 空串 “”
为了界定字符串,字符串在内存中存放时自动添加一个空字符’\0’作为串的结尾(该空字符可称为字符串的结束标志)。因此,存储字符串的存储空间中的字节数比双引号中的字符数多一个。为了界定字符串,字符串在内存中存放时自动添加一个空字符’\0’作为串的结尾(该空字符可称为字符串的结束标志)。因此,存储字符串的存储空间中的字节数比双引号中的字符数多一个。
我们应该注意字符常量与仅包含一个字符的字符串之间的区别:我们应该注意字符常量与仅包含一个字符的字符串之间的区别: • ’a’与”a”是不同的。 • ’a’表示一个字符,其值是字母a的ASCII码值,存储时占用1个字节的存储空间; • ”a”则是字符串,其中包含一个字符,存储时占用2个字节的存储空间,依次存放字符’a'和结束符’\0’。
【例】请分析下面哪些不是合法的字符常量‘C’、“C“、‘\xCC’、‘\072’【例】请分析下面哪些不是合法的字符常量‘C’、“C“、‘\xCC’、‘\072’ • 首先,根据字符常量的定义,所有的字符常量均是以单引号括起来,因此“C”不是合法的字符常量。‘C’是常见的字符常量的形式,不难看出它是合法的字符常量。 • 对于‘\xCC’和‘\072’,它们以‘\’打头,因此是字符转义序列,根据字符转义序列的定义,‘\xCC’表示十六进制形式的ASCII码为CC的字符,‘\072’表示八进制形式的ASCII码值为072的字符。因此它们均是合法的。
2.3 变量初始化 • 变量定义后,由于还没有给变量赋值,因此其值是不确定的。要想保证程序能够正确运行,变量在使用前应该进行赋值。赋值的方式有两种: (1)在变量定义的同时给它指定初始值; (2)在变量使用前进行赋值; • 第一种赋值方式我们又称为变量的初始化。其一般形式为: 类型标识符 变量名1=初值1, 变量名2=初值2, ….;
其中初值可以用和变量同类型的表达式提供,如果初值的类型和变量类型不一致,编译器会采用赋值运算的规则自动进行类型转换。其中初值可以用和变量同类型的表达式提供,如果初值的类型和变量类型不一致,编译器会采用赋值运算的规则自动进行类型转换。 int a = 2, b = 3; float a = 1.0, b; double a, b = 0.25; char a = ‘x’; • 注意,同时定义多个变量时,可以只对其中部分变量初始化。
请读者考虑是否能用以下方式进行变量初始化。请读者考虑是否能用以下方式进行变量初始化。 int a = b = 3; 如果不能,原因是什么?
2.4 运算符和表达式 • 2.4.1算术运算符 • 运算符%表示模运算(mod)或取余运算(rem)。表达式a%b的值是a除以b后的余数。例如10%2的值为0,10%3的值为1。
使用算术运算符时应注意: • %运算符要求两个操作数必须是整数,其它运算符允许操作数可以是整数或实数。 • 当操作数均是整型时,运算符/的计算结果也是整型,结果是通过舍去小数部分来得到。所以1/2的结果是0而不是0.5。 • 避免使运算符/和运算符%的第二个操作数为0。
使用算术运算符将操作数连接起来得到的式子称为算术表达式。使用算术运算符将操作数连接起来得到的式子称为算术表达式。 • 算术表达式的值就是算术运算符的计算结果。
【例】正整数m是一个3位数,按逆序输出m的3位数。【例】正整数m是一个3位数,按逆序输出m的3位数。 #include <stdio.h> void main( ) { int m,a,b,c; printf(“Input m:”); scanf(“%d”,&m); a=m%10; b=m/10%10; c=m/100; printf(“units digit:%d\n”,a); printf(“tens digit:%d\n”,b); printf(“hundreds digit:%d\n”,c); }
2.4.2运算符的优先级和结合性 • 1.关于优先级 • C语言中的运算符优先级共分15级,1级最高,15级最低。 • 算术运算符的优先级为: +(正号运算)和-(符号运算) 2级 *(乘)、 /(除)和 %(模运算) 3级 +(加)和-(减) 4级
2 .关于结合性 • 当表达式中含有多个优先级相同的运算符时,运算符的结合性开始起作用。 • 如果运算符是从左向右结合的,称这种运算符是左结合的。 • 如果运算符是从右向左结合的,称这种运算符是右结合的。 • 二元算术运算符都是左结合的,一元算术运算符都是右结合的。
2.4.3 赋值运算符 • 1.简单赋值运算符 • 简单赋值运算符用“=”表示,它是一个二元运算符。由“=”连接左右操作数形成的表达式称为赋值表达式。例如:a=1+2 • 赋值运算符的左操作数被称为“左值”,右操作数是一个表达式。 • 变量是左值,而常量和1+2、j*k、a=1等这样的表达式不是左值。 • 赋值表达式的值和类型等于赋值后的左操作数的值和类型。
赋值运算符的优先级是14,结合方向是自右向左。 a=(b=(c=1)) a=b=c=1
2.复合赋值运算符 • 复合赋值运算符包括: +=、-+、*=、/=、%=、<<=、>>=、^=、|= • 复合赋值运算符的优先级同样是14,结合方向也是自右向左。 a+=1 a=a+1 b-=c+1 b=b-(c+1) i=i*(j/k) i*=j/k a*=a+=2 a=a*(a=a+2)
【例】读取两个整数存入变量a、b,然后交换变量的值【例】读取两个整数存入变量a、b,然后交换变量的值 /*程序3-21*/ #include <stdio.h> void main( ) { int a,b,t; printf(“Input a and b:”); scanf(“%d%d”,&a,&b); printf(“Before exchange:a=%d,b=%d\n”,a,b); t=a; a=b; b=t; printf(“Aefore exchange:a=%d,b=%d\n”,a,b); }
2.4.4 自增、自减运算符 • 自增、自减运算符分别用“++”和“--”来表示。 • 自增、自减运算符是一元运算符,既可以作为前缀运算符,也可以作为后缀运算符。 例如:a=1;b=++a;a的值为2,b的值为2 a=1;b=a++;a的值为2,b的值为1
后缀自增、自减运算符的优先级是1级,结合方向是自左向右后缀自增、自减运算符的优先级是1级,结合方向是自左向右 • 前缀自增、自减运算符的优先级是2级,结合方向是自右向左。 j=-i++ j=-(i++)
注意: • 尽量避免在一个表达式中过多的使用自增、自减运算符,尤其是类似于(i++)+(i++)的表达式。 • 自增、自减运算符的操作数应是左值。变量是左值,而常量和i+j、i++、++i等表达式不是左值。所以表达式1++、(i+j)++、(i++)++、++ (++i)等都是错误的。
2.4.5 逗号运算符 • 用符号“,”表示, 用于连接两个表达式。 • 形如 左表达式,右表达式 的式子称为逗号表达式。 • 由逗号运算符连接的两个表达式从左向右依次计算。先计算左表达式的值,并且丢弃该值,再计算右表达式的值。 • 逗号表达式的值和类型取右表达式的值和类型。例如,表达式1+2,3+4的值为7。 • 逗号运算符的优先级最低。 i=1*2,3*4 (i=1*2),(3*4)
注意: • 逗号运算符明确规定了子表达式从左向右计算。例如,假设i=1,表达式a=i,i++的运算顺序是变量a先被赋值为1,然后变量i自增1。 • 逗号运算符会丢弃左表达式的值,但不会忽略左表达式的计算。例如表达式a=1,b=2将使变量a被赋值为1,变量b被赋值为2,表达式i++,j++将使变量i,j的值加1。
2.4.6 表达式语句 • 在C语言中,任何表达式都可以通过增加分号变成一条语句。 i++; j=++i; a=1+2; a+b;
2.5 数据类型转换 • 隐式转换 • 显式转换
2.5.1数据类型的隐式转换 • 隐式转换会在以下几种情况下发生: • 需要算术操作数的运算符的操作数类型不一致时,比如算术运算符、关系运算符要求操作数类型相同。此时进行一般算术转换。 • 赋值运算符的左操作数和右操作数类型不一致时。 • 函数调用中实际参数与对应的形式参数类型不一致时。 • 函数中return语句返回的值与函数返回值类型不一致时。