程序设计基础

第六章 数据类型和表达式

第6章 数据类型和表达式

6.1 数据的存储和基本数据类型
6.2 数据的输入和输出
6.3 类型转换
6.4 表达式

本章要点

  • C语言的基本数据类型有哪些?
  • 各种基本数据类型的常量有哪些表现形式?
  • C语言有哪些表达式?各种表达式的求解规则是什么

数据类型和表达式

运算,即指的对数据的操作,运算符+数据→表达式

6.1 数据的存储和基本数据类型

6.1.1 数据的存储--整型数据

假设在机器上,整数用内存中2个字节存储,即16个二进制位
最高位为符号位,0表示正数,1表示负数
则69和-69分别表示为
0000 0000 0100 0101
1000 0000 0100 0101

数值的表示方法--原码

符号位以外的二进制位表示整数的绝对值,不足的位数以"0"补足

例: 以16位二进制原码表示69和-69
 6910=(0000000001000101)2
-6910=(1000000001000101)2

数值的表示方法--反码

正整数的表示与原码相同,负整数的表示对数值位按位取反

例: 以16位二进制反码表示69和-69
以16位二进制原码表示69为6910=(0000000001000101)2
所以,反码表示为:
 6910=(0000000001000101)2
-6910=(1111111110111010)2

数值的表示方法--补码

正整数的表示与原码相同,负整数的表示对数值位按位取反,并加1

例: 以16位二进制反码表示69和-69
以16位二进制原码表示69为6910=(0000000001000101)2
所以,反码表示为:
 6910=(0000000001000101)2
-6910=(1111111110111011)2

原码 反码和补码

原码 反码 补码
+0 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
-0 1000 0000 0000 0000 1111 1111 1111 1111 0000 0000 0000 0000
+1 0000 0000 0000 0001 0000 0000 0000 0001 0000 0000 0000 0001
-1 1000 0000 0000 0001 1111 1111 1111 1110 1111 1111 1111 1111
32767(215-1) 0111 1111 1111 1111 0111 1111 1111 1111 0111 1111 1111 1111
-32767(-(215-1)) 1111 1111 1111 1111 1000 0000 0000 0000 1000 0000 0000 0001
-32768(-216) 1000 0000 0000 0000
32768(2!6) 0000 0001 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000

32767+1=?      -32768-1=? (溢出)

原码 反码和补码的表示范围

16个二进制位表示整数,表示范围分别为

  • 原码: -32767 ~ 32767
  • 反码: -32767 ~ 32767
  • 补码: -32768 ~ 32767

实型数据的存储

将整数表示成科学计数法,如-123.45写成科学计算法为-1.2345×102,可记成
-1.2345e+02
用32位浮点数表示,包含:符号位(1位),阶码(8位),尾数(23位)
用64位浮点数表示,包含:符号位(1位),阶码(11位),尾数(52位)

字符型数据的存储

字符型数据的存储采用ASCII码,存储在一个字节中
如字符常量'A'的ASCII码值为65,表示成
0100 0001

6.1.2 基本数据类型

类别 名称 类型名 数据长度 取值范围
整型 [有符号]整型 int 32 bits -231 ~ 231-1
[有符号]短整型 short[int] 16 bits -215 ~ 215-1
[有符号]长整型 long[int] 32 bits -231 ~ 231-1
无符号整型 unsigned[int] 32 bits 0 ~ 232-1
无符号短整型 unsigned short[int] 16 bits 0 ~ 216-1
无符号长整型 unsigned long[int] 32 bits 0 ~ 232-1
字符型 字符型 char 8 bits 0 ~ 255
实型
(浮点型)
单精度浮点型 float 32 bits ±(10-38 ~ 10+38)
双精度浮点型 double 64 bits ±(10-308 ~ 10308)

基本数据类型--整型

类别 名称 类型名 数据长度 取值范围
整型 [有符号]整型 int 32 bits -231 ~ 231-1
[有符号]短整型 short[int] 16 bits -215 ~ 215-1
[有符号]长整型 long[int] 32 bits -231 ~ 231-1
无符号整型 unsigned[int] 32 bits 0 ~ 232-1
无符号短整型 unsigned short[int] 16 bits 0 ~ 216-1
无符号长整型 unsigned long[int] 32 bits 0 ~ 232-1

整型常量(整数)

十进制 二进制 八进制 十六进制
正、负号
0~9
首位非0
正、负号
0~7
首位为0
正、负号
0~9,A~F
前缀0x
如1010 10 0000 1010 012 0xA
如1610 16 0001 0000 020 0X10
如12310 123 0111 1011 0173 0x7B

整数的取值不能超出整型数据的取值范围
比长整型数还要大的数用实数表示
用后缀表示整数类型,如123L表示长整数,123U表示无符号整数,123LU表示无符号长整数

基本数据类型--字符型

字符具有数值特征,字符变量和整型变量的定义和赋值可以在ASCII码范围内互换

                        
char c;
c='A'; //==>等价于 c=65;

int i;
i=65; //==>等价于 i='A';
                        
                    

字符常量

字符常量用单引号表示,如'a', 'A', '9', '+', '$'
所有的字符定义在ASCII字符集中,'0'~'9', 'A'~'Z', 'a'~'z'
可对字符进行运算,注意区分数字1和字符'1'

                        
char c;
c='A'; // c的值为65
printf("%d %c", c, c); // 输出 65 A
c=c+1; // c的值为66,对应字符'B'
printf("%d %c", c, c); // 输出 66 B
c=c+'1'; // c的值为115
printf("%d %c", c, c); // 输出 115 s
                        
                    

转义字符

  • 转义字符定义为反斜杠后跟一个字符或数字, 为字符常量,表示一个字符
  • 如'\n', '\101', '\x41', 'A'
  • 所有字符均可以用转义字符表示
字符 含义
\n 换行
\t 横向跳格(用于输出若干个空格)
\b 退格,用于显示输出时刷新左边一个字符
\\ 反斜杠字符'\'
\" 双引号
\' 单引号
\ddd 1-3位八进制数ddd所代表的字符,如\007为“嘟”声,\40为空格
\xhh 1-2位十六进制数ddd所代表的字符,如\x41为'A',\20为空格

基本数据类型--实型

类别 名称 类型名 数据长度 取值范围
实型
(浮点型)
单精度浮点型 float 32 bits ±(10-38 ~ 10+38)
双精度浮点型 double 64 bits ±(10-308 ~ 10308)

  • 浮点型数据的数据精度取值范围是两个不同的概念
  •                             
    float x=1234567.89; /* 数据在取值范围内,但无法精确表达 */
    float y=1.2e55; /* 数据的精度不高,但超出取值范围 */
                                
                            
  • 并不是所有的实数都能在计算机中精确表示
  • 实型常量的类型都是double
  • 实型常量表示法
    浮点表示法: 0.123, 123.4, 12., .12
    科学计数法: 6.025E-27, 1.2e+30, 1E-5

6.2 数据的输入输出

6.2.1 整型数据的输入输出

                        
printf(格式控制, 输出参数1, ..., 输出参数n);
scanf(格式控制, 输入参数1, ..., 输入参数n);
                        
                    
类别 十进制 八进制 十六进制
int %d %o %x
long %ld %lo %lx
unsigned %u %o %x
unsigned long %lu %lo %lx

输出整型数据示例

                        
#include<stdio.h>
int main()
{
    printf("%d, %o, %x\n", 10, 10, 10);
    printf("%d, %d, %d\n", 10, 010, 0x10);
    printf("%d, %x\n", 012, 012);
    return 0;
}
                        
                    

输入整型数据示例

                        
#include<stdio.h>
int main()
{
    int a, b;
    printf("Input a, b:");
    scanf("%x%d", &a, &b);
    printf("%d%5d\n", a, b); /* %5d指定变量b的输出宽度为5 */
    printf("%x, %d\n", a, b);
    return 0;
}
                        
                    

6.2.2 实型数据的输入输出

  • 输入: scanf()
    • float, %f%e,以小数或指数形式输入一个单精度浮点数
    • double, %lf%le,以小数或指数形式输入一个双精度浮点数
  • 输出: printf()
    • float和double采用相同的格式控制输出,%f以小数形式输出浮点数,保留6位小数;%e以指数形式输出浮点数

实型数据输出格式示例

                        
#include<stdio.h>
int main()
{
    double d=3.1415926;
    printf("%f, %e\n", d, d);
    printf("%5.3f, %5.2f, %.2f\n", d, d, d); /* 分别指明总位数和小数位数 */
    return 0;
}
                        
                    

实型数据的输入示例

假设float的精度为7位,double的精度为16位

                            
#include<stdio.h>
int main()
{
    float f;
    double d;
    printf("Input f, d:");
    scanf("%lf%lf", &f,  &d);
    printf("f=%f\n d=%f\n", f, d);
    d=1234567890123.12;
    printf("d=%f\n", d);
    return 0;
}
                            
                        

Input f, d:
1234567890123.123456
1234567890123.123456
f=1234567954432.000000
d=1234567890123.123540
d=1234567890123.120120

6.2.3 字符型数据输入输出

  • scanf和printf
  •                             
    char ch;
    scanf("%c", &ch);
    printf("%c", ch);
                                
                            
  • getchar和putchar
  •                             
    char ch;
    ch=getchar();
    putchar(ch);
                                
                            

输出输出字符示例

                            
#include<stdio.h>
int main()
{
    char ch1, ch2;
    ch1=getchar();
    ch2=getchar();
    putchar(ch1);
    putchar('#');
    putchar(ch2);
    return 0;
}
                            
                        
                            
#include<stdio.h>
int main()
{
    char ch1, ch2, ch3;
    scanf("%c%c%c", &ch1, &ch2, &ch3);
    printf("%c%c%c%c%c", ch1, '#', ch2, '#', ch3);
    return 0;
}
                            
                        

大小写英文字母转换

例6-1. 大小写英文字母转换

输入一行字符,将其中的大写字母转换为小写字母后输出,小写字母转换为相应的大写字母后输出,其它字符按原样输出

字符运算

大小写英文字母转换
'b'-'a'='B'-'A'
......
'z'-'a'='Z'-'A'
'm'-'a'+'A'==>'M'
'M'-'A'+'a'==>'m'

数字字符和数字转换
'9'-'0'=9-0
'9'='0'+9
......
'1'='0'+1

大小写英文字母转换源程序

                        
#include<stdio.h>
int main()
{
    char ch;
    printf("Input characters:");
    while((ch=getchar())!='\n'){
        if(ch>='A' && ch<='Z')
            ch=ch-'A'+'a';
        else if(ch>='a' && ch<='z')
            ch=ch-'a'+'A';
        putchar(ch);
    }
}
                        
                    

6.3 类型转换

不同类型数据的混合运算,先要将数据转换成相同的类型,再运算

6.3.1 自动类型转换

非赋值运算

自动转换类型:
取值范围小的→取值范围大的
整数→浮点
char, short → int
unsigned short → unsigned
long → unsigned long
float → double

int → unsigned → unsigned long → double

计算表达式'A'+12-10.05的结果,类型是?
'A' → 65, 65+12=77, 77-10.05=66.95, 类型为double

自动类型转换(赋值运算)

变量=表达式
计算赋值运算符右侧表达式的值
将赋值运算符右侧表达式的值赋给左侧的变量(自动转换右侧表达式的类型)

                        
double x;
x=1; /* x=? */

int ai;
ai=2.56; /* ai=? */

short a=1000;
char b='A';
long c;
c=a+b; /* c=? */

short bi;
bi=0X12345678L; /* bi=? */

                        
                    

强制类型转换

强制类型转换运算符: (类型名) 表达式

(double)3 → 3.0
(int)3.8 → 3
(double)(5/2) → 2.0
(double)5/2 → 2.5

强制类型转换示例

                        
#include<stdio.h>
int main()
{
    int i;
    double x;

    x=3.8;
    i=(int)x;
    printf("x=%.f, i=%d\n", x, i);
    printf("(double)(int)x=%f\n", (double)(int)x);
    printf("x mod 3=%d\n", (int)x%3);
    return 0;
}
                        
                    

6.4 表达式

表达式
由运算符和运算对象(操作数)组成的有意义的运算式子,它的值和类型由参与运算的运算符和运算对象的类型决定,其中,运算符指具有运算功能的符号;运算对象包含常量、变量和函数表达式等
表达式一般包含算术表达式、赋值表达式、关系表达式、逻辑表达式、条件表达式和逗号表达式等

6.4.1 算术表达式

算术运算符
目数 单目 双目
运算符 ++ -- + - + - * / %
名称 自增 自减 正值 负值 模(求余)
  • / 整数除以整数,结果为整数,如1/4=0, 10/3=3
  • % 模(求余),针对整型数据,如5%6=5, 9%4=1, 100%4=0
  • +/- 作为单目运算符时,+10, -10, 作为双目运算符时,x+10, y-10
  • 双目运算符两侧操作数的类型要相同,否则,自动进行类型转换后再运算

自增运算符++和自减运算符--

  • 使变量的值增1或减1
    ++n n++    n=n+1
    --n n--    n=n-1
  • 将变量的值作为表达式的值
    ++n     n=n+1, 将n的值作为表达式++n的值
    n++     将n的值作为表达式n++的值, n=n+1
                        
int n, m;
n=2; m=++n;  /* m=3, n=3 */
n=2; m=n++;  /* m=2, n=3 */
                        
                    

算术运算符的优先级和结合性

双目运算符(+, -)   →   双目运算符(*, /, %)   →   单目运算符(+, -, ++, --)

-5+3%2=(-5)+(3%2)=-4
3*5%3=(3*5)%3=0
-i++=-(i++)

写出下列表达式
s(s-a)(s-b)(s-c)   →   s*(s-a)*(s-b)*(s-c)
(x+2)e2x   →   (x+2)*pow(e,2*x)
$\frac{-b+\sqrt{b^2-4ac}}{2a}$  →   (-b+sqrt(b*b-4*a*c))/(2*a)

6.4.2 赋值表达式

变量=表达式

  • 计算赋值运算符右侧表达式的值
  • 将赋值表达式右侧表达式的值赋给左侧变量,右侧表达式的类型自动转换为左侧变量的类型
  • 将赋值表达式左侧变量的值作为表达式的值
  • 表达式结合从右往左
                        
int n;
double x, y;
x=3*4;  /* 表达式的值为12,类型为浮点双精度 */
x=y=3;  /* x=(y=3), y=3的值为3.0,类型为浮点双精度,x的值为3.0,类型为浮点双精度 */
n=3.14*2; /* n的值为6 */
x=10/4; /* x的值为2.0 */

                        
                    

复合赋值运算符

复合赋值运算符包括:+=, -=, *=, /=, %=
赋值表达式更新为: 变量   赋值运算符    表达式
x+= exp 等价于 x=x+exp
x*=y-3 等价于 x=x*(y-3)

6.4.3 关系表达式

关系运算符用于比较两个操作数,比较结果为真(1)假(0)
x<y    x <=y    x==y   
x>y    x>=y    x!=y

运算符优先级:
算术运算符 → 比较运算符(< <= >>=) → 比较运算符(== !=) → 赋值运算符

运算符左结合

a>b==c    →    (a>b)==c
d=a>b    →    d=(a>b)
ch>'a'+1    →    ch>('a'+1)
d=a+b>c    →    d=((a+b)>c)
3<=x<=5    →    (3<=x)<=5< /text>
b-1==a!=c    →    ((b-1)==a)!=c

关系表达式运算

关系运算符将2个表达式称为关系表达式,下列式子中哪些是关系表达式?

char c='w';
int a=2, b=3, c=1, d, x=10;
a>b==c    →    (a>b)==c    →    0
d=a>b    →    d=(a>b)    →    0
ch>'a'+1    →    ch>('a'+1)    →    1
d=a+b>c    →    d=((a+b)>c)    →   1
3<=x<=5    →    (3<=x)<=5    →    0
b-1==a!=c    →    ((b-1)==a)!=c    →    1

6.4.4 逻辑表达式

(ch>='a' && ch<='z') || (ch>='A' && ch <='Z')
ch == ' ' || ch == '\n'
x >= 3 && x <= 5

逻辑运算符

逻辑运算符为:&&(逻辑与) ||(逻辑或) !(逻辑非)
逻辑运算结果:1(真) 0(假)
逻辑运算对象: 关系表达式 或 逻辑量,如x>=3&& x <=5, !x
逻辑量,非0为真,0为假

逻辑运算真值表
a b a&&b a||b !a
1 1 真(1) 真(1) 假(0)
1 0 假(0) 真(1) 假(0)
0 1 假(0) 真(1) 真(1)
0 0 假(0) 假(0) 真(1)

逻辑运算符的优先级和结合性

赋值运算符 → 逻辑运算符(|| &&) → 关系运算符 → 算术运算符 → 逻辑运算符

运算符左结合

a || b && c    →    a || (b && c)
!a && b    →    (!a) && b
x >= 3 && x <= 5    →    (x >= 3) && (x <= 5)
!x == 2    →    (!x) == 2
a || 3 + 10 && 2    →    a || ((3 + 10) && 2)

逻辑表达式

逻辑运算符关系表达式逻辑量连接起来的式子称为逻辑表达式。下列式子中哪些是逻辑表达式?

char c='w';
int a=2, b=0, c=0;
float x=3.0;

a && b   →       →    0
a || b && c    →    a || (b && c)    →    1
!a && b    →    (!a) && b    →    0
a || 3 + 10 && 2    →    a || ((3 + 10) && 2)    →   1
!x == 2    →    (!x) == 2    →    0
!(x==2)    →       →    1
ch || b    →       →    1

写出满足要求的逻辑表达式

例6-4. 写出满足要求的逻辑表达式

  • x为零
  • x==0
    !x

  • x不为零
  • x!=0
    x

  • x和y不同时为零
  • x!=0 || y!=0
    !(x==0 && y==0)
    x||y

6.4.5 条件表达式

exp1 ? exp2 : exp3
等价于

                            
if(exp1)
    exp2;
else
    exp3;
                            
                        

条件表达式

分析下列条件表达式

                            
int n;
a=(n>0)?2.9;1;

                            
                        

n=10时, a=2.9
n=-10时, a=1

                            
if(a>b)
    z=a;
else
    z=b;
                            
                        

z=(a>b)?a:b;

$$y=\begin{cases} x+2 & x>0\\ x^2 & x\leq 0 \end{cases} $$

y=(x>0)?x+2:x*x;

6.4.6 逗号表达式

表达式1, 表达式2, ......, 表达式n

先计算表达式1,再依次计算表达式n的值 ,将表达式n的值作为逗号表达式的值

int a, b, c; (a=2), (b=3), (c=a+b);

逗号运算符的优先级最低,左结合

                        
for(i=0, sum=0; i<=100; i++)
    sum+=i;
                        
                    

6.4.7 位运算

位运算符
运算符 名称 备注
& 按位"与" 双目
| 按位"或" 双目
^ 按位"异或", 相同取0,不同取1 双目
~ 按位"取反" 单目
<<< /td> 左移位 双目
>> 右移位 双目

位逻辑运算

x = 0      00000000 00000000
y = 3      00000000 00000011
x & y      00000000 00000000
x | y        00000000 00000011
x ^ y       00000000 00000011
x && y    0
x || y        1
1010 ^ 0101 = 1111

注意区分&&和&,以及||和|

位移位运算

<<      对操作数左移给出的位数,低位补零
>>      对操作数右移给出的位数,高位补零

x<<3    将x向左移3位,空出的位用零补足
             00111010 <<3
             11010000

x>>3    将x向右移3位,空出的位用零补足
             00111010 >>3
             00000111

复合位赋值运算符

复合位赋值运算符有:&=    |=    ^=   >>=    <<=
a &= b 相当于 a = a & b
a <<= 2 相当于 a=a << 2

6.4.8 其他运算

长度运算符(sizeof),是单目运算符,用于计算变量数据类型的字节长度

                            
int a;
sizeof(a); // 求整型变量a的长度,值为4(bytes)
sizeof(int); // 求整型的长度,值为4(bytes)
sizeof(double); // 求双精度浮点数的长度,值为8(bytes)
                            
                        

6.4.9 程序解析

例6-5. 统计单词的个数

输入一行字符,统计其中单词的个数,其中,“单词”指的是连续不含空格的字符串,各单词之间用空格分隔,空格数可以是任意多个

统计单词个数源程序

                        
#include<stdio.h>
int main()
{
    int cnt, word;  /* cnt记录单词的个数,word表示新单词标识 */
    char ch;
    word=cnt=0;     /* word初值为0,表示还没有遇到新单词 */
    printf("Input characters:");
    while((ch=getchar())!='\n'){
        if(ch==' ')  /* 读入空格,表示不是单词 */
            word=0;  /* word赋0,表示没有遇到新单词 */
        else if(word==0){  /* 读入非空格且word为0,此为单词首字母 */
            word=1;  /* word赋1,表示遇到新单词 */
            cnt++;   /* 累加单词计数器cnt */
        }
    }
    printf("%d\n", cnt);
    return 0;
}