三、数据类型、运算符与表达式
3.1C语言的数据类型
基本类型:
-
整型:短整型(short int)、基本整型(int)、长整型(long int)
-
字符型(char)
-
浮点型:单精度型(float)、双精度型(double)、长双精度型(long double)
-
枚举类型(enum):它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
构造类型:
-
数组类型
-
结构体类型(struct)
-
共用体类型(union)
指针类型(*)
空类型(void):类型说明符 void 表示没有值的数据类型,通常用于函数返回值。
数组类型和结构类型统称为聚合类型。函数的类型指的是函数返回值的类型。
3.2常量和符号变量
常量:在程序运行过程中,其值不能被改变的量称为常量。
常量符号的使用:
#define PRICE 30
习惯上,符号常量名用大写,变量名用小写,以示区别。
使用符号常量的好处:
-
含义清楚。
-
在需要改变一个常量时能做到“一改全改”。
变量
变量代表内存中具有特定属性的一个存储单元,它用来存储数据,也就是变量的值,在程序运行期间,这些值是可以改变的。
(变量代表内存中的一个存储单元)
标识符就是一个名字。
C语言规定标识符只能由字母、数字和下划线3种字符组成,且第一个字符必须为字母或者下划线。
注意:编译系统将大写字母和小写字母认为是两种不同的字符。
在C语言中,要求对所有用到的变量作强制定义,也就是“先定义,后使用”。这样做的目的是:
-
凡未被事先定义的,系统不把它认作为变量名,这就能保证程序中变量名使用的正确。
-
每一个变量被指定为一个确定的类型,在编译时就能为其分配到相应的存储单元。
-
指定每一变量属于一个类型,这就便于在编译时据此检查在程序中要求对该变量进行的运算是否合法。
3.3整型数据
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。
整型常量即整常数。表现形式有:
-
十进制整数。如:123、-456、9。
-
八进制整数。以0开头的数是八进制数,如0123表示八进制数的123,即(123)8
-
十六进制整数。以0x开头的数是十六进制数
注意:存储时0正1负
int型的变量范围为:-215~(215-1),即-32768 ~ 32767
通常情况下,把long定为32位,把short定为26位,而int可以是16位也可以是32位。
整型数据的溢出P44
3.4浮点型数据
C语言中的浮点数就是平常所说的实数。浮点数有两种表现形式。
-
十进制小数形式。它由数字和小数点组成(注意必须要有小数点)。
-
指数形式。如123e3或123E3都代表123x103。但注意字母e(或E)之前 必须有数字,且e后面的指数必须为整数。
浮点型变量
浮点型数据在内存中的存放形式:一个浮点型数据一班在内存中占4个字节(32位)。与整型数据的存储方式不同,浮点型数据是按照指数形式存储的。系统把一个浮点型数据分成小数部分和指数部分,分别存放。指数部分采用规范化的指数形式。
小数部分占的位(bit)数愈多,数的有效数字愈多,精度也愈高。
指数部分占的位数愈大,则能表示的数值范围愈大。
浮点型变量的分类
类型 | 比特位(数) | 有效数字 | 数值范围 |
---|---|---|---|
float | 32 | 6~7 | -3.4x10-38 ~ 3.4x10-8 |
double | 64 | 15~16 | -1.7x10-308 ~ 1.7x10308 |
long double | 128 | 18~19 | -1.2x10-4932 ~ 1.2x104932 |
输出浮点数 : %f 、%lf
float只能保证的有效数字是7位,后面的数字是无意义的,因此并不能准确的表示小数位很多的浮点数。所以,我们应当避免一个很大的数和一个很小的数相加减。
3.5字符数据类型
C语言的字符常量是用单撇号括起来的一个字符。如'a'、'x'、'D'、'$'等都是字符常量。注意'a'和'A'是不同的字符常量。
除了以上形式的字符常量外,C语言还运行用一种特殊形式的字符常量,就是以一个字符""开头的字符序列。这是一种"控制字符",在屏幕上是不能显示的,在程序中也无法用一个一般形式的字符表示,只能采用特殊形式来表示。
常用的以“\”开头的特殊字符如下:
字符形式 | 含义 | ASCII码 |
---|---|---|
\n | 换行,将当前位置移到下一行开头 | 10 |
\t | 水平制表(跳到下一个Tab的位置) | 9 |
\b | 退格,将当前位置移到前一列 | 8 |
\r | 回车,将当前位置移到本行开头 | 13 |
\f | 换页,将当前位置移到下页开头 | 12 |
\ | 代表一个反斜杠字符“\” | 92 |
' | 代表一个单引号(撇号)字符 | 39 |
" | 代表一个双引号字符 | 34 |
\ddd | 1到3位八进制数所代表的字符 | |
\xhh | 1到2位十六制数所代表的字符 |
以上表中列出的字符称为“转义字符”,意思是将反斜杠“\”后面的字符转换成另外的意义。
字符变量
字符型变量用来存放字符常量,它只能放一个字符,不要以为在字符变量中就可以放一个字符串(包括若干字符)。
字符变量的定义如下:
//定义字符变量
char c1,c2;
//给所定义的字符变量赋值,一个字符变量在内存中各占一个字节。
c1 = 'a';
c2 = 'b';
字符数据在内存中的存储形式及其使用方法
将一个字符常量放到一个字符变量中,实际上并不是把改资费本身放到内存单元中去,而是将改资费的相应的ASCII码放到存储单元中。
既然在内存中,字符数据以ASCII码存储,它的存储形式就与整数的存储形式类似。这样使字符型数据和整型数据之间可以通用。一个字符数据可以以字符形式输出,也可以以整数形式输出。
1、向字符变量赋予整数
#include<stdio.h>
int main()
{
char c1,c2;
c1=97;
c2=98;
printf("%c %c\n",c1,c2); // a b
printf("%d %d\n",c1,c2); // 97 98
return 0;
}
2、大小写字母的转换
#include<stdio.h>
int main()
{
char c1,c2;
c1='a';
c2='b';
c1=c1-32;
c2=c2-32;
printf("%c %c\n",c1,c2); // A B
return 0;
}
程序的作用是将两个小写字母a和b转换成大写字母A和B。'a'的ASCII码为97,而'A'为65,'b'为98,'B'为66。从ASCII代码表中可以看出每一个小写字母都比它相应的大写字母的ASCII大32。C语言允许字符数据与整数直接进行算术运算。即'A'+32会得到整数97,'a'-32会得到整数65.
本小题个人小结之后代码改为:
#include<stdio.h>
int main()
{
char c1,c2;
c1='a';
c2='b';
c1=c1-('a'-'A');
c2=c2-('a'-'A');
printf("%c %c\n",c1,c2); // A B
//当我们忘记字符对应的ASCII值是多少时,我们可以这样写,直接将字符以整型%d的格式输出,结果就为该字符对应的ASCII值
printf("\'a\'-\'A\'的ASCII值相差为%d\n",'a'-'A');
return 0;
}
字符串常量
字符串常量是由一对双撇号括起来的字符序列。(要区别于前面的字符)
这是是合法的字符串常量:"How do you do." 、"CHINA" 、"a" 、"#!afaff"等
输出一个字符串:
printf("How do you do." );
注意:不要将字符常量和字符串常量混淆。'a'是字符常量,"a"是字符串常量,二者不同。
char c='a'; //正确
char c="a"; //错误,"a"是字符串常量,不能将其赋值给字符常量c
小知识:'a'和"a"究竟有什么区别?P52
C规定:在每一个字符串常量的结尾加一个“字符串结束标志”,以便系统判断字符串是否结束。C规定以字符'\0'作为字符串结束标志,'\0'是一个ASCII为0的字符,从ASCII代码表中可以看到ASCII为0的字符是“空字符操作”,即它引起任何控制作动,也不是一个可显示的字符。如果有一个字符串常量"CHAIN",实际上在内存中是:
它占内存单元不是5个字符,而是6个字符,最后一个字符为'\0'。但在输出时不输出'\0'。
注意:在书写字符串的时候是不用加'\0',否则会画蛇添足。'\0'字符四系统自动加上的。字符串"a"实际上包含两个字符:'a'和'\0',因此,想把它赋值给只能容纳一个字符的字符变量c显然是不行的。
在C语言中没有专门的字符串变量,如果想将一个字符串存放在变量中以便保存,必须使用字符数组,即用一个字符型数组来存放一个字符串,数组中每一个元素存放一个字符。
3.6变量赋初值
int a = 3; //指定a为整型变量,初值为3
float f = 3.14; //指定f为浮点型变量,初值为3.14
char c = 'a'; //指定c为字符变量,初值为'a'
//也可以使被定义的变量的一部分赋初值。例如
int a, b, c = 5;
//对几个变量赋予同一个初值,应写成:
int a = 3; b = 3; c = 3; //表示a、b、c的初值都是3 int a = b = c =3; 这种写法是错误的。
3.7各类数值型数据间的混合运算P54
整型(包括int、short、long)和浮点型(包括float、double)可以混合运算。但是,在进行运算时候,不同类型的数据要先转换成同一类型,然后进行计算。转换的规则按下图所示:
注意:箭头方向只表示数据类型级别的高低,由低向高转换。
3.8算术运算符和算术表达式
C语言运算符简介
C语言的运算符范围很宽,把除了控制语句和输入输出以外的几乎所有的基本操作作为运算符处理,例如将赋值符号“=”作为赋值运算符、方括号作为下标运算符等。C的运算符有以下几类:
运算符 | 例如 |
---|---|
算术运算符 | + - * / % |
关系运算符 | > < == >= <= != |
逻辑运算符 | ! && || |
位运算符 | << >> ~ | ^ & |
赋值运算符 | =及其扩展赋值运算符 |
条件运算符 | ?: |
逗号运算符 | , |
指针运算符 | *和& |
求字节运算符 | sizeof |
强制类型转换运算符 | (类型) |
分量运算符 | . -> |
下标运算符 | [ ] |
其他 | 如函数调用运算符() |
注意:5/3=1 整除;5%3=2 模运算或者求余运算
强制类型转换
一般表达式为:(类型名)(表达式)
(double)a ; //将a强制转换成double类型
(int)(x+y) ; //将x+y的值强制转换成int类型
(float)(5%3) ; //将5%3的值强制转换成float类型
样例代码:
#include<stdio.h>
int main()
{
float x;
int i;
x = 32767.0;
i=(int)x%3;
printf("x=%f,i=%d\n",x,i); //x=32767.000000,i=1
return 0;
}
// x类型仍为float类型,其值为32767.0
自增自减运算符
作用是使变量的值增1或减1。例如:
++i; //在使用i之前,先使i的值加1
--i; //在使用i之前,先使i的值减1
i++; //在使用i之后,使i的值加1
i--; //在使用i之后,使i的值减1
注意:自增运算符(++)和自减运算符(--)只能用于变量,而不能用于常量或者表达式,如5++或(a+b)++都是不合法的。因为5是常量,常量的值是不能改变的。(a+b)++也不可能实现,假如a+b的值为5,那么自增后得到的6放在什么地方呢?无变量可供存放。
3.9赋值运算符和赋值表达式
赋值运算符
赋值符号“=”称为赋值运算符,它的作用是将一个数据赋一个变量。
类型转换P60
如何赋值运算符两侧的类型不一致,但都是数值型或者字符型时,在赋值时要进行类型转换。转换方法在不同系统中有些差别。
复合赋值运算符
a += 3; // 等价于 a = a + 3
x *= y + 8; //等价于 x = x * ( y + 8 )
x %= 3; //等价于 x = x % 3
赋值表达式
又赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”。它的一般形式为
变量 赋值运算符 表达式
如“a=5"是一个赋值表达式。对赋值表达式求解的过程是:先求赋值运算符右侧的“表达式”的值,然后赋给赋值运算符左侧的变量。一个表达式应该有一个值,例如,赋值表达式" a = 3*5" 的值为15,变量a的也是15。赋值运算符左侧的标识符称为"左值"。并不是任何对象都可以作为左值,因为字符常量不能被赋值。左值都可以作为右值。
赋值表达式的例子
a = b = c = 5; //赋值表达式的值为5,a、b、c的值均为5
a = 5 + (c = 6); //表达式的值为11,a的值为11,c值为6
a = (b = 4) + (c = 6); //表达式的值为10,a的值为10,b等于4,c等于6
a = (b = 10)/(c = 2); //表达式的值为5,a等于5,b等于10,c等于2
3.10逗号运算符和逗号表达式
C语言提供一种特殊的运算符——逗号运算符。用一个逗号将两个表达式连接起来。
例如: 3+5,6+8
这是一个逗号表达式。逗号运算符又称为“顺序求值运算符”。
逗号表达式的一般形式为:表达式1,表达式2
逗号表达式的求解过程是:先求解表达式1,再求解表达式2。整个逗号表达式的值是表达式2的值。例如,上面的逗号表达式“3+5,6+8”的值为14。
一个逗号表达式又可以与另一个表达式组成一个新的逗号表达式。例如:
(a = 3 * 5 , a * 4), a+5
//先算出a的值为3*5,等于15,再进行a*4,的运算等于60,(但a的值未变,仍为15),
//再进行a+5等于20,即整个表达式的值为20
逗号表达式的一般形式可扩展为:
表达式1,表达式2,表达式3,... ... ,表达式n
它的值为表达式n的值。
逗号运算符是所有运算符中级别最低的。因此下面两大表达式的作用是不同的:
x = (a=3,6*3) //这是一个赋值表达式,将一个逗号的表达式的值赋给x,x的值为18
x = a = 3,6*3 //逗号表达式,包括赋值表达式和一个算术表达式,x的值为3,整个表达式的值为18
其实,逗号表达式无非是把若干个表达式“串联”起来。在许多情况下,使用逗号表达式的目的是想分别得到各个表达式的值,而并非一定需要得到和使用整个逗号表达式的值,逗号表达式最常用于循环语句(for语句)中。
注意,并不是任何地方出现的逗号都是作为逗号运算符。例如函数参数也是用逗号运算符间隔的。
课后习题
请将C语言的数据类型和其他高级语言的数据类型作比较。c有哪些特点?
c语言的数据类型丰富,表达能力强,应用灵活。例如:c语言提供了“结构体”这样一种数据结构。而fortran语言就不提供这种数据结构。C语言的数据类型主要有:基本类型(包括整型,字符型,浮点型,和枚举类型),构造类型(包括数组类型,结构体类型,共用体类型),指针类型,空类型。利用以上数据类型还可以构成更复杂的数据结构。如:利用指针和结构体类型可以构成表,树,栈等复杂的数据结构。
写出以下程序的运行结果:
#include<stdio.h>
int main()
{
char c1='a',c2='b',c3='c',c4='\101',c5='\116';
printf("a%cb%c\tc%c\tabc\n",c1,c2,c3); //aabb cc abc
printf("\t\b%c%c\n",c4,c5); //AN
return 0;
}
// %c 为输出字符 单独的字符原样输出
// \t为制表符(通常为一个tab的位置)、\b退格,将当前位置移到前一列(可以理解为向左移动了一个位置)
//最终,输出的是AN,前面可能有一个或多个空格(取决于\t的实际宽度和\b的退格效果)
#include<stdio.h>
int main()
{
int i,j,m,n;
i=8;
j=10;
m=++i; //在使用i之前,i的值先加1,所以m=9,i=9
n=j++; //在使用j之后,j的值再加1,所以n=j=10,j=11
printf("%d,%d,%d,%d\n",i,j,m,n); //9,11,9,10
return 0;
}
写出下面赋值表达式运算后a的值,设原来a=12
a += a; // a = a + a = 24
a -= 2; // a = a - 2 = 10
a *= 2+3; // a = a * (2+3) = 60
a /= a + a; // a = a / (a+a) = 12/24 =0.5 而/是整除 所以结果为0
a %= (n %= 2); //(n的值为5) 内模 n = n%2 = 1;则 a %= 1; 算得a=0
a += a -= a *= a; // 答案为0 ,先算a *= a,再算a -= a,最后算a += a(再次注意,a的值在前面的步骤中可能已经变得不确定了)
注意:在C语言中,当表达式如
a += a;
出现在一个复杂的表达式序列中,并且这个序列涉及到对a
的多次修改而没有明确的序列点(sequence point)来分隔这些修改时,a
的值在表达式执行过程中会变得不确定。然而,在你给出的具体情况下
a += a;
(假设它是独立执行的,或者至少是在一个不包含其他对a
修改的表达式中),这个表达式本身并不会导致a
的值变得不确定。表达式a += a;
实际上是a = a + a;
的简写形式,它会将a
的当前值与其自身相加,并将结果赋值回a
。问题出现在你之前的表达式
a += a -= a *= a;
中。这个表达式包含了多个对a
的修改,而C语言标准没有规定这些修改的精确顺序(即没有序列点来分隔它们)。因此,编译器可以自由地以任何顺序来评估这个表达式,这导致了未定义行为。
本章小结
C语言中的关键字
C语言中具有特殊含义的单词
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
char | short | int | long | float | double | if | else |
return | do | while | for | switch | case | break | continue |
default | goto | sizeof | auto | register | static | extern | unsigned |
signed | typedef | struct | enum | union | void | const | volatile |
特点:
-
全部小写
-
不可用作变量名、函数名等
-
C语言一共有32个关键字
声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》和《C程序设计试题汇编(第三版)》如有错误,欢迎大家指正!