第三周 数据和C

第三周 数据和C

3.1 示例程序

程序清单3.1 platinum.c程序

/* platinum.c  -- your weight in platinum */
#include <stdio.h>
int main(void)
{
     float weight;    /* 你的体重                */
     float value;     /* 相等重量的白金价值        */

     printf("Are you worth your weight in platinum?\n");
     printf("Let's check it out.\n");
     printf("Please enter your weight in pounds: ");//输入你的体重,然后按下Enter或Return键

     /* 获取用户的输入                        */
     scanf("%f", &weight); //scanf()函数读取用户从键盘输入的数据,并把数据传递给程序
     /* 假设白金的价格是每盎司$1700             */
     /* 14.5833用于把英磅常衡盎司转换为金衡盎司[1]*/
     value = 1700.0 * weight * 14.5833;
     printf("Your weight in platinum is worth $%.2f.\n", value);
     printf("You are easily worth that! If platinum prices drop,\n");
     printf("eat more to maintain your value.\n");

     return 0;
}

3.1.1 程序中的新元素

  • float类型可以存储带小数的数字。
  • printf()中使用%f来处理浮点值。
  • %.2f中的.2用于精确控制输出,指定输出的浮点数只显示小数点后面两位。
  • scanf()函数用于读取键盘的输入。%f说明scanf()要读取用户从键盘输入的浮点数,&weight告诉scanf()把输入的值赋给名为weight的变量。scanf()函数使用&符号表明找到weight变量的地点。
  • 本程序最突出的新特点是它的交互性。计算机向用户询问信息,然后用户输入数字。

3.2 变量与常量数据

数据,即承载信息的数字和字符。

常量(constant):在程序使用之前已经预先设定好了,在整个程序的运行过程中没有变化的数据类型。

变量(variable):在程序运行期间可能会改变或被赋值的数据类型。

3.3 数据:数据类型关键字

C通过识别一些基本的数据类型来区分和使用不同的数据类型。

最初K&R给出的关键字C90标准添加的关键字C99标准添加的关键字
intsigned_Bool
longvoid_Complex
short_Imaginary
unsigned
char
float
double

int:表示基本的整数类型。

longshortunsignedsigned:用于提供基本整数类型的变式,例如unsigned short intlong long int

char关键字用于指定字母和其他字符(如,#$%*)。另外,char类型也可以表示较小的整数。

floatdoublelong double表示带小数点的数。

_Bool类型表示布尔值(truefalse)。

_Complex_Imaginary分别表示复数和虚数。

位、字节和字

位(bit)是最小的存储单元,可以存储0或1(或者说,位用于设置“开”或“关”)。位是计算机内存的基本构建块。

字节(byte)是常用的计算机存储单位。对于几乎所有的机器,1字节均为8位。

字(word)是设计计算机时给定的自然存储单位。对于8位的微型计算机(如,最初的苹果机),1个字长只有8位。从那以后,个人计算机字长增至16位、32位,直到目前的64位。计算机的字长越大,其数据转移越快,允许的内存访问也更多。

3.3.1 整数和浮点数

对我们而言,整数和浮点数的区别是它们的书写方式不同。

对计算机而言,它们的区别是存储方式不同。

3.3.2 整数

整数是没有小数部分的数。例如,2、−23和2456都是整数。而3.14、0.22和2.000都不是整数。

计算机以二进制数字存储整数,例如,整数7以二进制写是111。因此,要在8位字节中存储该数字,需要把前5位都设置成0,后3位设置成1

img

3.3.3 浮点数

浮点数与数学中实数的概念差不多。2.75、3.16E7、7.00和2e-8都是浮点数。

注意,在一个值后面加上一个小数点,该值就成为一个浮点值。所以,7是整数,7.00是浮点数。

浮点数和整数的存储方案不同:

计算机把浮点数分成小数部分和指数部分来表示,而且分开存储这两部分。因此,虽然7.00和7在数值上相同,但是它们的存储方式不同。

两种类型的实际区别。

  • 整数没有小数部分,浮点数有小数部分。
  • 浮点数可以表示的范围比整数大。
  • 对于一些算术运算(如,两个很大的数相减),浮点数损失的精度更多。
  • 因为在任何区间内(如,1.0到2.0之间)都存在无穷多个实数,所以计算机的浮点数不能表示区间内所有的值。浮点数通常只是实际值的近似值。例如,7.0可能被存储为浮点值6.99999。
  • 过去,浮点运算比整数运算慢。不过,现在许多CPU都包含浮点处理器,缩小了速度上的差距。

3.4 C语言基本数据类型

3.4.1 int类型

int类型是有符号整型,即int类型的值必须是整数,可以是正整数、负整数或零。

其取值范围依计算机系统而异。一般而言,存储一个int要占用一个机器字长。因此,早期的16IBM PC兼容机使用16位来存储一个int值,其取值范围(即int值的取值范围)是-3276832767

ISO C规定int的取值范围最小为-3276832767

1.声明int变量

先写上int,然后写变量名,最后加上一个分号。

int erns; // 可以单独声明变量
int hogs, cows, goats; // 也可在int后面列出多个变量名,变量名之间用逗号分隔。

给变量赋值:

1种途径是赋值。例如,cows = 112;

2种途径是,通过函数(如,scanf())获得值;

3种途径初始化变量

2.初始化变量

初始化(initialize)变量就是为变量赋一个初始值。

在C语言中,初始化可以直接在声明中完成。只需在变量名后面加上赋值运算符(=)和待赋给变量的值即可。

int hogs = 21;
int cows = 32, goats = 14;
int dogs, cats = 94; /* 有效,但是这种格式很糟糕 */

简而言之,声明为变量创建和标记存储空间,并为其指定初始值

.img

3.int类型常量

上面示例中出现的整数(21321494)都是整型常量或整型字面量。

22-44都是整型常量,但是22.02.2E1则不是。

C语言把大多数整型常量视为int类型,但是非常大的整数除外。

4.打印int

可以使用printf()函数打印int类型的值。

/* print1.c - 演示printf()的一些特性 */
#include <stdio.h>
int main(void)
{
     int ten = 10;
     int two = 2;

     printf("Doing it right: ");
     printf("%d minus %d is %d\n", ten, 2, ten - two);
     printf("Doing it wrong: ");
     printf("%d minus %d is %d\n", ten);  // 遗漏2个参数

     return 0;
}

编译并运行该程序,输出如下:

Doing it right: 10 minus 2 is 8 ①
Doing it wrong: 10 minus 16 is 1650287143

①在第一行输出中,第1%d对应int类型变量ten;第2%d对应int类型常量2;第3%d对应int类型表达式ten - two的值。

②在第二行输出中,第1%d对应ten的值,但是由于没有给后两个%d提供任何值,所以打印出的值是内存中的任意值

记住,使用printf()函数时,要确保转换说明的数量与待打印值的数量相等。

5.八进制和十六进制

通常,C语言都假定整型常量是十进制数。

在C语言中,用特定的前缀表示使用哪种进制。

0x或0X前缀表示十六进制值,所以十进制数16表示成十六进制是0x10或0X10。

0前缀表示八进制。例如,十进制数16表示成八进制是020。

6.显示八进制和十六进制

在C程序中,既可以使用也可以显示不同进制的数。不同的进制要使用不同的转换说明。

  • 以十进制显示数字,使用%d

  • 以八进制显示数字,使用%o

  • 以十六进制显示数字,使用%x

要显示各进制数的前缀00x0X,必须分别使用%#o%#x%#X

程序清单3.3 bases.c程序

/* bases.c--以十进制、八进制、十六进制打印十进制数100 */
#include <stdio.h>
int main(void)
{
     int x = 100;

     printf("dec = %d; octal = %o; hex = %x\n", x, x, x);//十进制、八进制、十六进制
     printf("dec = %d; octal = %#o; hex = %#x\n", x, x, x);//在八进制和十六进制值前显示0和0x前缀,要分别在转换说明中加入#。

     return 0;
}

编译并运行该程序,输出如下:

dec = 100; octal = 144; hex = 64
dec = 100; octal = 0144; hex = 0x64

3.4.2 其他整数类型

C语言提供3个附属关键字修饰基本整数类型:shortlongunsigned。应记住以下几点。

  • short int类型(或者简写为short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。与int类似,short是有符号类型。
  • long intlong占用的存储空间可能比int多,适用于较大数值的场合。与int类似,long是有符号类型。
  • long long intlong longC99标准加入)占用的存储空间可能比long多,适用于更大数值的场合。该类型至少占64位。与int类似,long long是有符号类型。
  • unsigned intunsigned只用于非负值的场合。这种类型与有符号类型表示的范围不同。例如,16unsigned int允许的取值范围是065535,而不是-3276832767。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。
  • C90标准中,添加了unsigned long intunsigned longunsigned short intunsigned short类型。C99标准又添加了unsigned long long intunsigned long long
  • 在任何有符号类型前面添加关键字signed,可强调使用有符号类型的意图。例如,shortshort intsigned shortsigned short int都表示同一种类型。
1.声明其他整数类型

其他整数类型的声明方式与int类型相同,下面列出了一些例子。不是所有的C编译器都能识别最后3条声明

long int estine;
long johns;
short int erns;
short ribs;
unsigned int s_count;
unsigned players;
unsigned long headcount;//C90标准新增
unsigned short yesvotes;//C90标准新增
long long ago;//C99标准新增
2.使用多种整数类型的原因

C语言只规定了short占用的存储空间不能多于intlong占用的存储空间不能少于int。这样规定是为了适应不同的机器。

现在,个人计算机上最常见的设置是,

  • long long64位,

  • long32位,

  • short16位,

  • int16位或32位(依计算机的自然字长而定)。

原则上,这4种类型代表4种不同的大小,但是在实际使用中,有些类型之间通常有重叠。

C标准对基本数据类型只规定了允许的最小大小。

  • 对于16位机,shortint的最小取值范围是[32768,32767]

  • 对于32位机,long的最小取值范围是[2147483648,2147483647]

  • 对于unsigned shortunsigned int,最小取值范围是[0,65535]

  • 对于unsigned long,最小取值范围是[0,4294967295]

  • long long类型是为了支持64位的需求,最小取值范围是[9223372036854775808,9223372036854775807]

  • unsigned long long的最小取值范围是[0,18446744073709551615]

(这个数是一千八百亿亿六千七百四十四万亿零七百三十七亿零九百五十五万一千六百一十五)

int类型那么多,应该如何选择?

首先,考虑unsigned类型。这种类型的数常用于计数,因为计数不用负数。而且,unsigned类型可以表示更大的正数。

  • 如果一个数超出了int类型的取值范围,且在long类型的取值范围内时,使用long类型。

然而,对于那些long占用的空间比int大的系统,使用long类型会减慢运算速度。如非必要,请不要使用long类型。

  • 如果在long类型和int类型占用空间相同的机器上编写代码,当确实需要32位的整数时,应使用long类型而不是int类型,以便把程序移植到16位机后仍然可以正常工作。类似地,如果确实需要64位的整数,应使用long long类型。

  • 如果在int设置为32位的系统中要使用16位的值,应使用short类型以节省存储空间。

通常,只有当程序使用相对于系统可用内存较大的整型数组时,才需要重点考虑节省空间的问题。

使用short类型的另一个原因是,计算机中某些组件使用的硬件寄存器是16位。

3.long常量和long long常量

程序代码中使用的数字(如,2345)都被存储为int类型。

如果使用1000000这样的大数字,超出了int类型能表示的范围,编译器会将其视为long int类型(假设这种类型可以表示该数字)。

如果数字超出long可表示的最大值,编译器则将其视为unsigned long类型。

如果还不够大,编译器则将其视为long longunsigned long long类型(前提是编译器能识别这些类型)。

八进制和十六进制常量被视为int类型。

如果值太大,编译器会尝试使用unsigned int

如果还不够大,编译器会依次使用longunsigned longlong longunsigned long long类型。

把一个较小的常量作为long类型对待,可以在值的末尾加上l(小写的L)或L后缀。

因此,在int16位、long32位的系统中,会把7作为16位存储,把7L作为32位存储。

lL后缀也可用于八进制和十六进制整数,如020L0x10L

类似地,在支持long long类型的系统中,也可以使用llLL后缀来表示long long类型的值,如3LL

另外,uU后缀表示unsigned long long,如5ull10LLU6LLU9Ull

整数溢出

如果整数超出了相应类型的取值范围会怎样?

/* toobig.c-- 超出系统允许的最大int值*/
#include <stdio.h>
int main(void)
{
     int i = 2147483647; 
      unsigned int j = 4294967295;

      printf("%d %d %d\n", i, i+1, i+2);
      printf("%u %u %u\n", j, j+1, j+2);//使用%u说明显示unsigned int类型的值

      return 0;
}

在本地系统下输出的结果是:

2147483647    -2147483648    -2147483647
4294967295    0    1

可以把无符号整数j看作是汽车的里程表。当达到它能表示的最大值时,会重新从起始点开始。整数i也是类似的情况。

它们主要的区别是,在超过最大值时,unsigned int类型的变量j0开始;而int类型的变量i则从−2147483648开始。

注意,当i超出(溢出)其相应类型所能表示的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。

4.打印shortlonglong longunsigned类型

打印unsigned int类型的值,使用%u转换说明;

打印long类型的值,使用%ld转换说明。

如果系统中intlong的大小相同,使用%d就行。但是,这样的程序被移植到其他系统(intlong类型的大小不同)中会无法正常工作。

xo前面可以使用l前缀,%lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。

注意,虽然C允许使用大写或小写的常量后缀,但是在转换说明中只能用小写。

C语言有多种printf()格式。

对于short类型,可以使用h前缀。

  • %hd表示以十进制显示short类型的整数
  • %ho表示以八进制显示short类型的整数。

hl前缀都可以和u一起使用,用于表示无符号类型。例如,%lu表示打印unsigned long类型的值。

程序清单3.4 print2.c程序

/* print2.c--更多printf()的特性 */
#include <stdio.h>
int main(void)
{
     unsigned int un = 3000000000; /* int为32位和short为16位的系统 */
     short end = 200;
     long big = 65537;
     long long verybig = 12345678908642;

     printf("un = %u and not %d\n", un, un);//①
     printf("end = %hd and %d\n", end, end);//②
     printf("big = %ld and not %hd\n", big, big);//③
     printf("verybig= %lld and not %ld\n", verybig, verybig);//④对于支持long long类型的系统,%lld和%llu分别表示有符号和无符号类型。

     return 0;
}

在特定的系统中输出如下(输出的结果可能不同):

un = 3000000000 and not -1294967296
end = 200 and 200
big = 65537 and not 1
verybig= 12345678908642 and not 1942899938

使用错误的转换说明会得到意想不到的结果:

①第1行输出,对于无符号变量un,使用%d会生成负值!

原因是,无符号值3000000000和有符号值−1294967296在系统内存中的内部表示完全相同

因此,如果告诉printf()该数是无符号数,它打印一个值;如果告诉它该数是有符号数,它将打印另一个值。在待打印的值大于有符号值的最大值时,会发生这种情况。对于较小的正数(如96),有符号和无符号类型的存储、显示都相同。

②第2行输出,对于short类型的变量end,在printf()中无论指定以short类型(%hd)还是int类型(%d)打印,打印出来的值都相同。

因为在给函数传递参数时,C编译器把short类型的值自动转换成int类型的值。

为什么要进行转换?

int类型被认为是计算机处理整数类型时最高效的类型。因此,在shortint类型的大小不同的计算机中,用int类型的参数传递速度更快。

h修饰符有什么用?

使用h修饰符可以显示较大整数被截断成short类型值的情况。第 3 行输出就演示了这种情况。

③把 65537 以二进制格式写成一个 32 位数是00000000000000010000000000000001。使用%hdprintf()只会查看后16位,所以显示的值是1

④与此类似,输出的最后一行先显示了verybig的完整值,然后由于使用了%ldprintf()只显示了存储在后32位的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值