C Primer Plus(第五版)3

第三章 数据和 C

在本章中你将学习下列内容:

1. 关键字:
int, short, long, unsigned, char, float, double, _Bool,
_Complex, _Imaginary

2. 运算符:
sizeof

3. 函数
scanf()

4. C 使用的基本数据类型。

5. 整数类型和浮点类型区别的区别。

6. 对上述类型,如何书写常量和声明变量。

7. 使用 printf() 和 scanf()函数读写各种类型数据的值。

3.1 示例程序

这里仍以一个示例程序作为开端。正如前面章节那样,我们将解释你感到不熟悉的地方。该程序的大致意图应该是很清晰的,因此请试着编译并运行程序清单 3.1 中的源代码。为了节省时间,输入源代码时,可略去注释。

程序清单 3.1 rhodium.c程序
-----------------------------------------------------------------------
/* rhodium.c --- 用金属铑衡量你的体重 */
#include <stdio.h>
int main (void)
{
float weight; //用户的体重
float value; //相等重量的铑的价值
printf (" Are you worth your weight in rhodium?\n");
printf ("Let's check it out.\n");
printf ("Please enter your weight in pounds: ");

scanf ("%f",&weight); // 从用户处获取输入 此处为这个示例的关键处

/* 假设铑为每盎司 770美元, 14.5833 把常衡制的英镑转换为金衡制的盎司 */
value = 770 * weight * 14.5833;
printf ("your weight in rhodium is $%.2f.\n",value); //$%.2f 看仔细这个输出参数
printf ("You are easily worth that! If rhodium prices drop,\n");
printf ("eat more to maintain your value.\n");
getchar();
getchar();
return 0;
}
-------------------------------------------------------------------------

Are you worth your weight in rhodium?
Let's check it out.
Please enter your weight in pounds: 150
your weight in rhodium is $1684371.12.
You are easily worth that! If rhodium prices drop,
eat more to maintain your value

此程序中的新元素

此程序中包含 C语言如下的一些新元素

1. 请注意代码中使用了一种新的变量声明。前面例子中只有整数变量(int),而本例中还包含了一个 浮点变量(float)类型,以便处理更大范围内的数据。float类型可以处理带有小数点的数字。

2. 程序还示范了常量的几种新写法,你现在就可以使用带有小数点的数了。

3. 要打印这种新的变量类型,请在 printf()代码中使用 %f 说明符来处理浮点值。对 %f 说明符
使用 .2 修饰词 即(%.2f)可以精确控制输出格式,使浮点数显示到小数点后两位。

4. 使用 scanf() 函数为程序提供键盘输入。%f 指示 scanf()从键盘读取一个浮点数,
&weight 指定将输入值赋于名为 weight 的变量中。scanf()函数使用 & 符号指示 weight变量
的位置。下一章将进一步讨论 & 符号,现在请相信此处你需要它。

5. 也行本程序最突出的新特点是它的交互性。计算机向你询问信息,并使用你输入的数字。与非交互 性程序相比较,交互性程序使用起来更为有趣。更重要的是,交互性方法使程序更加灵活。

本章解释上述新特性列表中的前两项:各种数据类型的变量和常量。第4章“字符串和格式化输入/输出”将介绍后3项,本章极将继续使用 scanf()和 Printf()的有限功能。

3.2 变量与常量数据

在程序的指示下,计算机可以做很多事情,比如数值计算,名字排序,执行语音或视频命令,计算彗星轨道,准备邮寄列表,拔电话号码,画图,做决策以及任何其他可以想像到的事。要完成这些任务,程序需要使用数据,即承载信息的数字与字符。有些数据可以在程序使用之前预先设定并在整个运行过程中没有变化,这称为常量。另外的数据在程序运行过程中可能变化或被赋值,这称为变量。在示例程序中 weignt 是变量,而14.5833 则是常量。 770.0呢?虽然铑的价格不是一成不变的,但此程序中把它作为常量来对待。变量与常量的区别在于,就是的值可以在程序执行过程中变化与指定,而常量则不可以。

3.3 数据: 数据类型关键字

原来的 K&R 关键字 C90关键字 C99关键字
int signed _Bool
long void _Complex
short _Imaginary
unsigned
char
float
double
-----------------------------------------------------------------------------------

int关键字提供 C使用的基本整数类型。下面3个关键字(long,short和 unsigned)以及 ANSI附加的 signed用于提供基本类型的变种。char关键字用于表示字母以及其他字符(如#,%,和*)。
char类型也可以表示小的整数。

float,double和组合 long double表示带有小数点的数。

_Bool类型表示布尔值(true 和 false)。

_Complex 和 _Imaginary 分别表示复数和虚数。

这些类型可以按其在计算机中的存储方式被划分为两个系统,即整数(integer)和浮点数(floating-point)类型。

-----------------------------------------------------------------------------------
PS: 位,字节和字

术语:位,字节和字 用于描述计算机数据单位或机存储单位。这里主要指存储单位。

最小的存储单位称为 位(bit)。它可以容纳两个值(0或1)之一(或者可以称该位置被置为“关”或“开”)。不能在一个位中存储更多的信息,但是计算机中包含数量极其众多的位。位是计算机存储的基本单位。

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

对于一种给定的计算机设计,字(word)是自然的存储单位。对于8位微机,比如原始的 Apple机,一个字正好有8位。使用80286处理器的早期 IBM兼容机是 16位机,这意味着一个字的大小为 16位。基于 Pentium 的 PC机和 Macintosh PowerPC 中的字是32位。更强大的计算机可以有 64位甚至更长位数的字。

3.3.1 整数类型与浮点数类型

对于人,整数和浮点数的区别在于它们的书写。对于计算机,区别在于它们的存储方式。下面分别地它们进行介绍。

3.3.2 整数

整数 (integer)就是没有小数部分的数。在 C中,小数点永远不会出现在整数的书写中。例如 2,-23 和 2456都是整数。数 3.14, 0.22 和2.000都不是整数。整数以二进制数字存储。例如整数 7的进制表示为 111,在 8位的字节中存储它需要将前 5位置0, 将后 3位置 1,如下图所示

图3.2 使用二进制编码存储整数 7
-----------------------------------------------------------------------
0 0 0 0 0 1 1 1 ---- 8位长的字
2^2 2^1 2^0
4 + 2 + 1 = 整数 7

-----------------------------------------------------------------------

3.3.3 浮点数

浮点数(floating-point)差不多可以和数学中实数(real number)概念相对应。实数包含了整数之间的那些数。2.75,3.16E7,7.00和 2e-8 都是浮点数。注意,加了小数点的数是浮点型值,所以 7 是整数类型,而 7.00是浮点型。显然,书写浮点数有多种形式。本书将在后面介绍 e 记数法,这里仅做简要介绍:简单地说,3.16E7 表示 3.16乘以 10的 7次方(即 1后面带有 7个 0),7称为 10的指数。

这里最重要的一点是浮点为数与整数的存储方案不同。浮点数表示法将一个数分为小数部分和指数部分并分别存储。因此尽管 7.00和整数 7有相同的值,但它们的存储方式不同。与机器中的二进制存储方式相似,在十进制中 7.0 可表示为 0.7E1,这里 0.7是小数部分,1 是指数部分。图3.3所示为浮点数存储的另一个例子。当然,计算机的内部存储使用二进制数字,它使用 2的幂而非 10的幂。在第 15章可以找到有关这一主题的更多讨论,这里我们只关注这两种类型在应用中的区别:

图 3.3 以浮点格式存储实数 (十进制版本)
---------------------------------------------------
+ .314159 1
符号 小数部分 指数部分
+ .314159 X 10^1 = 3.14159

----------------------------------------------------

浮点数往往只是实际值的近似。例如,7.0可能以浮点值 6.99999存储。稍后我 们将讨论更多有关精度的内容。

3.4.1 int 类型

C 提供多种整数类型。你可能不明白为什么一种类型不够用,答案是 C 为程序员提供了针对不同用途的多种选择。具体来讲,C 的各种整数类型的区别在于所提供数值的范围,以及数值是否可以取负值。int类型是基本选择,你还可以根据任务和机器的特殊需求选择其他类型。

int 类型是有符号整数,即 int类型的值必须是整数,可以是正的,负的或者是 0,其聚会范围依赖于计算机系统。一般地,int类型存储在计算机的一个字中。旧的 IBM PC兼容机有16位的字,因而使用 16位来存储一个 int值,取值范围为 -32768 到 32767 ,目前的个人计算机上的整数一般有 32位,使用 32位的 int值,详见本章结尾处的表 3.4 。现在,个人计算机向着 64位的处理器发展,自然而然将要使用更大的整数。 ISO/ANSI C规定 int类型的最小范围是 -32768 到 32767 。一般地,系统通过使用一个指示正负符号的特定位来表示有符号整数。第15章将讨论常用的方法。

一. 声明 int变量

int erns;
int hogs,cows,goats;

可以分别声明每个变量,也可以在一条语句中声明所有的 4个变量。效果是一样的,都将为 4个int大小的变量赋予名称并安排存储空间。

以上变量声明创建了变量但没有为其赋值。如果为变量赋值?前文已经出现了两种为变量赋值的方法。首先是直接赋值:

cows = 112;

其次,可以通过 scanf()这样的函数为变量赋值。下面介绍第三种方法。

二. 初始化变量

初始化(initialize)变量就是为变量赋一个初始值。C语言中,可以在声明语句中初始化变量,即在变量名后跟上赋值运算符(=)和要赋给变量的值,如下所示:

int hogs = 21;
int cows = 32, goats = 14;
int dogs,cats = 94; //该语句有效,但这种形式不是很好

最后一行中,只对 cats 进行了初始化。这种写法会让人误以为 dogs 也被初始化为94,所以最好避免在一个声明语中同时出现初始化和末初始化变量。

简言之,声明语句为变量创建,标定存储空间并为其指定初始值,如图 3.4所示

图 3.4 定义和初始化变量
-------------------------------------------------------------------------
int sows; -- 分配存储空间 □□□□□□□
|----------------------------------|

int boars = 2; -- 分配存储空间并赋值 □□□2□□□
|----------------------------------------|

--------------------------------------------------------------------------

三. int 类型常量

上面例子中的整数 21,32,14 和 94都是整数常量。 C把不含小数点和指数的数当作整数,比如 22 和 -44 都是整数常量, 而 22.0 和 2.2E1 则不是。 C把大多数整数常量看作 int类型。如果整数特别大,则有不同的处理。详细信息请参见后面的“long常量和 long long常量”小节中关于
long int类型的介绍。

四. 打印 int 值

可以使用 printf ()函数打印 int类型的值。在第2章我们已经介绍过中 %d 符号用于指示在一行中什么位置打印整数。%d 被称为格式说明符 (format specifier),因为它指示 printf()应使用什么格式来显示一个数值。格式串中的每个 %d 都必须对应于打印项目列表中的一个 int 值。这个值可以是 int变量,int常量或者其他的值为 int类型的表达式。你必须确保格式说明符的数目同待打印值的数目相同,编译器不会发现这种类型的错误。

程序清单 3.2是一个简单的程序,它初始化一个变量,并且打印了这个变量的值,一个常量的值以及一个简单表达式的值。 它也演示了当你粗心犯错时会导致什么结果。

程序清单 3.2
-----------------------------------------------------
/* printl.c -- 说明 printf()的一些属性 */
#include <stdio.h>
int main (void)
{
int ten = 10;
int tow = 2;

printf("Doing it right:");
printf("%d minus %d is %d\n",ten,2,ten-tow);
printf("Doing it wrong:");
printf("%d minus %d is %d\n",ten); //忘掉二个参数
getchar();
return 0;
}

编译并运行上面的程序,则系统输出下列结果:
Doing it right: 10 minus 2 is 8
Doing it wrong: 10 minus 2 is 10

在第一行的输出语句中,第 1个 %d对应 int变量 ten,第 2个 %d对应 int常量 2,第 3个 %d对应 int表达式 ten-two的值。但第2次,程序使用 ten为第1个 %d提供打印值,然后使用内存中的任意值为其余的两个 %d 提供了打印值(你在运行该程序时获得的数值会不同于这里显示的数。不仅是因为内存中的内容可能不同,而且因为不同的编译器处理的内存位置不同)。

你可能会为编译器查不出这样明显的错误而烦恼,抱怨 printf()的非常规设计。大多数函数有确切的参数数目,编译器可以检查数目是否正确。然而,printf()可以有1个,2个,3个或更多的参数,这使得编译器无法使用常规的方法检查错误。记住,使用printf()函数时,格式说明符的数目和要显示的值的数目一定要相同。

五. 八进制和十六进制

前缀 0x 或者 0X表示使用十六进制,所以 16用十六进制表示为 0x10 或者 0X10 。与之相似,前缀0(零)表示使用八进制。例如 十进制数16 用八进制表示为 020 。关于进制,会在第15章详细介绍。

要清楚,这种使用不同数制系统的选择是为了方便而提供的,它并不影响数字的存储。无论,16,020还是 0x10,数字都按照同样的方式,即计算机内部使用的二进制编码进行存储。

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

C既允许你使用 3种数制书写数字也允许以这 3种数制显示数字。要用八进制而不是十进制显示数字,请用%o (字母o)。要显示十六进制整数,请使用 %x 。如果想显示 C语言前缀,可以使用说明符 %#o, %#x 和 %#X 分别生成 0,0x,0X 前缀。程序清单 3.3是一个简单的例子

程序清单 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); //dec 十进制,octal 八进制, hec 十六进制
printf ("dec = %d; octal=%#o; hex=%#x\n",x,x,x);
getchar();
return 0;
}

编译并运行上面的程序,将产生下列输出
dec = 100; octal = 144; hec = 64;
dec = 100; octal = 0144; hec = 0x64

程序用 3种不同的数制系统显示同一个值。printf()函数做了相关的转换。注意,要显示 0 和 0x前缀,必须在说明符中加入 #符号。
---------------------------------------------------------------------------------------

3.4.2 其他整数类型

初学语言时,int类型会满足你对整数的大多数需求。但为了给出完整的介绍,现在我们将讨论其他类型。你也可以跳过本节,直接阅读“使用字符:char类型”小节,在以后需要的时候再阅读本节。

C提供 3个附属关键字修饰基本的整数类型:short,long 和 unsigned 。应当记住以下几点:

1. short int类型(或者简写为 short类型)可能占用比 int类型更少的存储空间,用于仅需小数值
的场合以节省空间。同 int类型一样,short类型是一种有符号类型。

2. long int类型(或者简写为 long类型)可能占用比 int类型更多的存储空间,用于使用大数值的场 合。同 int类型一样,long类型是一种有符号类型。

3. long long int 类型(或者简写为 long long类型)(都是在C99标准中引入的),可能占有用比
long更多的存储空间,用于使用更大数值的场合。同 int类型一样,long long类型是一种有符号
类型

4. unsigned int类型(或者简写为 unsigned类型)用于只使用非负值的场合。这种类型同有符号的表 示范围不同。例如,16位的unsigned int 取值范围为 0 到65535,而带符号 int的取值范围为
-32768 到 32767. 由于指示数值正负的位也被用于二进制位,所以无符号数可以表示更大的数值。

5. 在 C90标准中,还允许 unsigned long int (简写 unsigned long)和 unsigned short int
(简写 unsigned short)类型。 C99又增加了 unsigned long long int(简写为 unsigned
long long)类型。

6. 关键字 signed 可以和任何有符号类型一起使用,它使数据的类型更加明确。例如:short,
short int, signed short 以及 signed short int 代表了同一种类型。

一. 声明其他整数类型

其他整数类型的声明方式同 int类型相同,下面是一些例子。一些早期的 C语言编译器不识别最后 3条语句,最后一条语句由 C99标准最新引入。

long int estine;
long johns;

short int erns;
short ribs;

unsigned int s_count;
unsigned players;

unsigned long headcount;
unsigned short yesvotes;

long long ago;

二. 使用多种整数类型的原因

为什么说 long 和 short类型“可能”占用比 int 类型更多或者更少的存储空间呢?因为 C仅保证 short 类型不会比 int类型长,并且 long类型不会比 int类型短。这样做是为了适应不同的机器。例如,在一台运行 Windows 3.1的 IBM PC上,short类型和 int类型都是 16位,long类型是 32位。而在一台 Windows XP机器或 Macintosh PowerPC 上, short类型是 16位,int类型和 long类型都是 32位。Pentium 芯片 和 PowerPC G3或 G4芯片的自然字大小都是 32位,这使整数可以表示大于 20亿的数。在以上处理器和操作系统的组合中实现 C时,实现都认为没有表示更大数的需要,因此 long类型使用和 int类型同样的长度。很多场合不需要这么大的整数,因而创建了更节省空间的 short类型。另一方面,早期的 IBM PC中字长只有 16位,这意味着在它上面的 C实现需要比 int类型更大的 long类型。

现在 64位处理器,如IBM Itanium, AMD Opteron 和 Power PC G5 正变得越来越普遍,需要 64位的整数,因而引入 long long 类型。

目前一般的情况是,long long类型为64位,long类型为 32位,short类型为 16位,int类型为 16位或 32位(依机器的自然字大小而定)。但原则上,这 4种类型代表 4个不同大小的值。

C 语言标准规定了每种基本数据类型的最小取值范围,对应于 16位单位,short类型 和 int类型的最小取值范围为 -32767 到 32767;

对应于 32位单位,long类型的最小取值范围为 -2147483647 到2147483647(注意,为了便于理解,这里使用了逗号,但是在 C代码中不允许这样)。

对于 unsigned short类型 和unsigned int类型,最小取值范围为 0 到 65535;

对于 unsigned long类型,最小取值范围为 0到 4294967295。

long long类型是为了支持 64位的需求,最小取值范围是数目可观的 -9223372036854775807 到
9223372036854775807;

unsigned long long 类型的最小取值范围为 0到 18446744073709551615.

在诸多整数类型中选择哪一种呢?请首先考虑 unsigned类型。把这种类型用于计数是十分自然的事,因为此时你不需要负数,而且无符号类型可以取得比有符号类型更大的正数。

当使用 int类型不能表示一个数而使用 long类型可以做到时,使用 long类型。但是,在 long类型大于 int类型的系统中,使用 long类型会减慢计算,所以没有必要时不要使用 long类型。如果是在 long类型等于 int类型的系统中编写代码,当确实需要 32位整数时,就使用long类型(而不是 int类型)以便使程序被移植到 16位机器上后仍然可以正常工作。

与之类似,如果需要 64位整数,你应使用 long long类型。一些计算机已经使用了 64位处理器,并且 64位的服务器,工作站甚至桌面系统不久将十分普遍。

在 int 为32位的系统上,如果需要 16位的值,那么使用 short类型可以节省存储空间。通常,只有当程序使用了使系统可用内存很紧张的较大的整数数组时,节省存储空间才是重要的。使用 short类型的另一个原因是计算机中的一些硬件寄存器是 16位的。

----------------------------------------------------------------------------------
ps : 整数溢出

如果整数太大,超出了整数类型的范围会怎么样?下面分别将有符号类型和无符号类型整数设置为最大允许值加略大一些的值,看看结果是什么(printf()函数使用 %u 说明符显示 unsigned int类型的值)。

/* 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);
getchar();
return 0;
}

下面是我们使用的系统的结果:
2147483647 -2147483648 -2147483647
4294967295 0 1

无符号整数 j 像一个汽车里程指示表,当达到最大值时,它将溢出起始点。 整数 i 也是同样。他们主要的区别是 无符号变量 j 的起始点是 0, 而 int 变量 i 的起始起点则是 -2147483648 。注意
当 i 超过(溢出)它的最大值时,系统并没有给出提示,所以编程时你必须自己处理这个问题。

这里描述的现象由 C中关于无符号类型的规则所操纵, C标准没有定义有符号类型的溢出规则。这里的现象是比较有代表性的,但你也可能遇到不同的情况。

--------------------------------------------------------------------------------------

三. long常量和 long long常量

通常,在程序代码中使用 2345这样的数字时,它以 int类型存储。当使用 1000000 这样的数字 int类型不能表示时,编译器会视其为 long int类型(假定这种类型可以表示该数字)。如果数字大于 long 类型的最大值,C 会视其为 unsigned long类型。如果仍然不够,C 会视其为 long long类型或者 unsigned long long 类型(如果有这些类型的话)。

八进制和十六进制常量通常被视为 int类型。如果值过于大,编译器会试用 unsigned int,如果不够在,编译器会依次试用 long, unsigned long, long long 和 unsigned long long 类型。

有时候你也许会希望编译器用 long类型来存储一个较小的整数。例如,在编程中涉及到显式使用 IBM PC上的内存地址时,就是会产生这样的需求。一些标准的 C函数也需要 long类型的值。如果希望把一个较小的常量作为 long类型对待, 可以使用 l (小写的L)或 L后缀。使用 L后缀是更好的选择,因为 l同数字 1很相近。这样,在 int类型为 16位,long类型为 32位的系统中,会把整数 7作为 16位数存储,而把整数 7L作为 32位数存储。 l和 L后缀对八进制和十六进制数同样适用,比如 020L
0x10L。

与之类似,在支持 long long 类型的系统中,可以使用 ll 或 LL后缀标识 long long 类型值,比如 3LL。 u 或 U后缀用于标识 unsigned long long 类型值,比如 5ull ,10LLU, 6LLU 和 9Ull。

四. short,long,long long 和 unsigned 类型数

要打印 unsigned int 数字,可以使用 %u符号,打印 long数值,可以使用 %ld格式说明符。如果系统 int 和 long类型具有同样的长度,使用 %d就可以打印 long数值,但是这会给程序移植到其他系统(这两种数据类型的长度不一样的系统)带来麻烦,所以建议使用 %ld打印 long数值。 在 x 和 o 符号前也可以使用 l 前缀, 因此 %1x表示以十六进制格式打印长整数, %lo表示以八进制打印长整数。请注意,尽管在 C中常量后缀可以使用大写和上写,但格式说明符只能使用小写字母。

C 还有其他几种 printf()格式。首先,可以对 short 类型使用 h前缀,因为 %hd表示以十进制显示 short整数,%ho表示以八进制显示 short整数。 h 和 l 前缀都可以同 u 结合使用以表示无符号类型。 比如, %lu表示打印 unsigned long 类型。 程序清单 3.4给出了一个例子。支持 long long类型的系统使用 %lld 和 %llu分别表示有符号类型和无符号类型。 第4章将详细介绍格式说明符。

程序清单 3.4 print2.c程序
-----------------------------------------------------------------------
/* print2.c -------------printf()的更多属性 */
#include <stdio.h>
int main (void)
{
unsigned int un = 3000000000; // int 为32位
short end = 200; // short 为 16位的系统
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);
getchar();
return 0;
}

下面是在某系统上的执行结果
un = 3000000000 and not - 1294967296
end = 200 and 200
big = 65537 and not 1
verybig = 12345678908642 and not 1942899938

这个例子就表明如果使用了不正确的说明符,会造成意想不到的后果。首先,对无符号变量 un使用 %d说明符会导致显示负值!这是由于在程序运行的系统中,无符号数 3000000000 和有符号数 -1294967296在内存中的表示方法是一样的 (详见第 15章。)所以,如果告诉 printf()函数该数值是无符号的,它将打印某个值,而如果告诉 printf()函数该数值是有符号的,它将打印另外一个值。在数值大于有符号类型最大值的时候会发生这种情况。对于小一些的正数(比如 96),有符号和无符号类型的存储和显示都是相同的。

其次,不论使用 %hd 还是 %d,short类型变量 end的显示结果相同。这是因为在传递函数参数时 C自动将 short类型的值转换为 int类型。这会在你的脑子里引起两个疑问:为什么要进行这样的转换?
h 修饰符的用处是什么? 第一个问题的答案是:int 类型被认为 计算机处理起来最方便有效的整数类型,所以在 short类型和 int类型长度不同的系统中,使用 int类型值进行参数传递的速度更快;第二个问题的答案是:可以使用 h修饰符显示一个较长的整数被截为 short 类型值时的样子。输出的第三行就演示了这一点。把值 65537按照二进制格式写一个32位的数字时,它应该是 0000000000000001
0000000000000001 。在 printf()中使用 %hd说明符将使它只显示后 16位,即显示值 1。

与此类似,最后一行输出先昧了 verybig 变量的完整值,然后通过使用 %ld说明符显示了存储在它的后32位中的值。

前面你已经认识到应该确保说明符的数目与要显示的值的数目相匹配。这里说明了还必须根据要显示的值的类型来选用正确的说明符。
--------------------------------------------------------------------------------------

PS : 匹配 printf()说明符的类型

使用 printf()语句时,切记每个要显示的值都必须对应自己的格式说明符,并且显示值的类型要同说明符相匹配。

3.4.3 使用字符:char类型

char类型用于存储字母和标点符号之类的字符。但是在技术实现上 char却是整数类型,这是因为 char类型实际存储的是整数而不是字符。为了处理字符,计算机使用一种数字编码,用特定的整数表示特定的字符。美国最常用的编码是 ASCII码,这张表在本书封二给出来了。本书也使用此编码。在
ASCII码中,整数值 65代表大写字母A;因此要存储字母A,实际只需要存储65(许多 IBM主机使用另一种称为 EBCDIC的编码,但其原理是相同的。其他国家的计算机系统也许会使用完全不同的编码)。

标准 ASCII码值的范围从 0 到 127,只需 7位即可表示。而 char类型通常定义为使用 8位内存单元,该大小容纳标准 ASCII编码是绰绰有余的。许多系统提供的不同的扩展 ASCII编码也是使用 8位存储单元。更普遍一些来看,C保证 char类型足够大,以存储其实现所在的系统上的基本字符集。

许多字符集包含先多于 127甚至远多于 255个值,商用的 Unicode字符集建立了一个能够表示世界范围内多种字符集的系统,目前已有超过 96 000个字符。国际标准化组织和国际电工技术委员会为字符集开发了 ISO/IEC 10646标准。幸运的是,Unicode标准保持了同更广泛的 ISO/IEC 10646标准的兼容书生。

采用上术这集合之一作为基本字符集的平台应该使用 16位甚至 32位的 char表示方法。 C把一个字节(byte)定义为 char类型使用的位(bit)数。(前面的int使用字作为基本储存)所以在这样的系统上,C文档中提到的一个字节是 16位或者 32位,而不是 8位。

一·声明 char 类型变量

正如你所预料的那样,char变量同其他类型变量的声明方式相同,下面是一些例子:

char response;
char itable,latan;

这段代码创建了 3个 char变量:response, itable 和 latan。

二·字符常量及其初始化

假定你要把一个字符常量初始化为字母 A。计算机语言应该使事情更为简单,因此你无须记住字符中 ASCII码。可以使用下列初始化语句把字符 A 赋给 grade;

char grade = 'A';

单引号中的一个字符是 C的一个字符常量,编译器遇到‘A’时会将其转换为相应的编码值,其中单引号是必不可少的。看另外一个例子:

char broiled; //声明一个 char 变量
broiled = 'T'; //可以
broiled = T; // 不可以!看T看作一个变量
broiled = "T"; // 不可以!把“T”看作一个字符串

如果不使用单引号,编译器会将 T视为一个变量名;如果使用双引号,编译器将其视为一个字符串。我们将在第4章讨论字符口中。

因为字符实际上以数值的形式存储,所以也可以使用数值编码来赋值:

char grade = 65; //对于 ASCII,这是可以的,但这是一种不好的编程风格

上面的语句中,65是 int类型,但是它在 char类型大小范围之内,所以这样的赋值完全允许。由于65是字母 A 的 ASCII码,此语句将字符 A赋予变量 grade。但是要注意,这个结果的假设是系统使用 ASCII码。而使用‘A’代替 65进行赋值则可在任意系统中正常工作。因此,推荐使用字符常量,而不是数值编码。

令人奇怪的是,C将字符常量视为 int类型而非 char类型。例如,在 int类型为 32位和 char类型为 8位ASCII系统中,下列代码:

char grade =‘B’;

意味着‘B’作为数值 66存储在一个 32位单元中,而赋值后的 grade则把 66存储在一个 8位单元中。利用字符常量的这个特性,可以定义一个字符常量‘FATE’,这将把 4个独立的 8位 ASCII码存储在一个 32位单元中。然而,如果把空上字符常量赋给一个 char变量,那么只有最后 8位会起作用,因此变量的值为‘E’。

三·非打印字符

单引号技术适用于字符,数字和标点符号,但是如果浏览一下本书封二的 ASCII表,你会发现有些 ASCII 字符是打印不出来的。例如一些动作描述:退格,换行或者让终端铃响(或扬声器蜂鸣)。怎么表示这些字符?C提供了 3种方法。

我们已经提到过第一种方法,就是使用 ASCII码。例如,蜂鸣字符的 ASCII值为 7,所以可以这样写: char beep =7;

第二种方法是使用特殊的符号序列,即转义序列(Escape Sequence)。表 3.2列出了转义序列及其意义。

----------------------------------------------------
表3.2 转义序列

\a 警报(ANSIC)
\b 退格
\f 走纸
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
\\ 反斜杠(\)
\' 单引号(')
\" 双引号(")
\? 问题(?)
\0oo 八进制值(o表示一个八进制数字)
\xhh 十六进制值(h表示一个十六进制数字)

------------------------------------------------------
给一个字符变量进行赋值时,转义序列必须用单引号括起来。例如,可以使用下列语句:

char nerf =‘n’;

这样,打印变量 nerf 在打印屏幕上将表现为换行。

现在我们来研究一下每个转义序列的功能。 警报字符 \a (由C90新增)产生一个能听到或能看到的警报,这取决于计算机的硬件,蜂鸣是最常见的警报(在一些系统中警报不起作用)。ANSIC标准规定警报字符不改变系统的活动位置。活动位置(active position)即在显示设备(屏幕,电传打字机,打印机等等)中下一个字符将出现的位置。也就是说,程序中把警报字符输出到屏幕上,将只发出一声蜂鸣而并不移动屏幕光标。

接下来,转义序列 \b, \f, \n, \r, \t 和 \v 是常用的输出设备控制字符。说明它们的最好方法是描述它们对活动位置的影响。退格符 \b使活动位置在当前行上退回一个空格。 走纸符 \f将活动位置移到到下一页的开始处。 换行符 \n换活动位置移到下一行的开始处。回车符 \r将活动位置移到当前行的开始处。水平制表符 \t将活动位置移到下一个水平制表点(通常为字符位置 1,9,17,25等等)
垂直制表符 \v将活动位置移到下一个垂直制表点。

这些转义字符不一定适用于所有设备。例如,走纸符和垂直制表符在 PC屏幕上产生奇怪的符号,而不会产生任何光标的移动,它们只有在输出到打印机上时才会像前面描述的那样工作。

下面三个转义序列 \\, \' 和 \" 使你可以引用 \, '和 " 字符常量(由于这些符号被作为 printf()命令的一部分用来定义字符常量,所以如果你在字面上使用他们时会造成混乱)。如果要打印下面这行内容:

gramps sez,“a \ is a backslash.”

可以使用如下代码

printf ("Gramps sez,\"a \\ is a backslash,\ " \n");

最后两个转义字符 \0oo 和 \xhh 是ASCII码的专用表示方法。如果想用一个字符的 八进制 ASCII码代表它,可以在编码值前加一个反斜杠 (\)并用单引号引起来。例如:如果编译器不识别警报字符 (\a),则可以使用 ASCII 码代替:

beep = ‘\007’;

可以省去前面的 0,就是说'\07' 和 '\7'都可以。即使没有前缀 0,这种写法仍会使数值被解释为八进制数 。

从 C90 开始,C提供了第三种选择,即使用十六进制形式表示字符常量。在这种形式中,反斜杠后跟一个 x 或 X,再加上 1到 3位十六进制数字。例如, Ctrl+P 字符的十六进制 ASCII码值为 10(相当于十进制值中的 16),它可以表示为 ‘\x10’或‘\X010’。

使用 ASCII码时要注意数字和数字字符的区别。例如,字符 4的ASCII码值为 52.写法‘4’表示符号4而不是数值 4.

关于转义序列,你可能会有如下三个疑问:

1. 为什么在上一例子( printf ("Gramps sez,\"a \\ is a backslash,\" \n");)中,转义序列没有用单引号引起来呢?无论是普通字符还是转义序列,如果用为双引号中字符集合的一部分,则无需单引号。该例中的其他字符(G,r,a,m,p,s,等等)也没有单引号引起来。双引号中的字符集合称为字符串(详见第4章。)与之类似, printf("Hello!\007 \n");语句将打印 Hello!并发出一声蜂鸣,而 printf("hell!7\n");语句则打印 Hello !7.。不在转义字符中的数字将普通字符那样被打印出来。

2. 什么时候使用 ASCII 码,什么时候使用转义序列呢?如果要在某个转义序列和与其对应的 ASCII码之间做出选择,则应当使用转义序列。比如选择 '\f' 而不是'\014'。首先,转义字符更容易记忆,其次,这样做使程序的可移植性更好。因为在不使用 ASCII 码的系统中,‘\f 仍然适用。

3. 当需要使用数值编码时,为什么使用 '\032' 而不 032? 首先,'\032'能更清晰地表达程序员表示一个字符编码的意图;其次,'\032' 这样的转义序列可以嵌入到 C字符串中,比如字符串 “Hello
! \007\n”中就嵌入了 '\007'。

四·打印字符

printf()函数使用 %c说明符打印一个字符。回忆一下,字符变量被存储为 1字节长的号数值。因而,如果使用通常的 %d 说明符打印 char变量,将得到一个整数。%c格式说明符告诉 printf()函数打印编码值等于那个整数的字符。程序清单 3.5显示了 char变量的两种打印方法。

程序清单 3.5 charcode.c程序
-------------------------------------------------------------------------------
/* charcode.c --- 显示一个字符的编码值 */
#include <stdio.h>
int main (void)
{
char ch;
printf (" Please enter a character.\n");
scanf ("%c", &ch);
printf ("The code for %c is %d.\n",ch,ch);
return 0;
}
下面是一个运行示例:
lease enter a character
C
The code for c is 67.

运行此程序,在键入字母后不要忘记按 Enter或 return键。随后 scanf()函数将读取你键入的字符,&符号指示指导输入的字符赋给变量 ch,接着 printf()函数把 ch的值打印两次。首先作为字符打印(由代码中的 %c指示),然后作为十进制打印(由代码中的 %d指示)。注意 printf()说明符决定数据的显示方式而不是数据的存储方式。

五·有符号还是无符号

一些 C实现把 char当作有符号类型。这意味着 char类型值的典型范围是 -128 到 127 。另一些 C实现把 char 当作无符号类型,其取值范围为 0 到 255 。编译器会指明 char的类型,或者你可以通过 limits.h头文件检查一下,下一章将对该文件做详细介绍。

根据 C90标准, C允许在关键字 char前使用 signed 和 unsigned 。这样,无论默认的 char类型是什么,signed char 是有符号类型, unsigned char 则是无符号类型。这对于使用字符类型处理小整数十分有用。如果处理字符,则只须使用不带修饰词的标准 char类型。

3.4.4 _Bool类型

_Bool类型 由C99引入,用于表示布尔值,即逻辑值 true(真)与 false (假)。因为 C用值 1表示 true,用值 0表示 false。所以 _Bool类型实际上也是一种整数类型。只是原则上它仅仅需要 1位来进行存储。因为对于 0 和 1 而言, 1位的存储空间已经够用了。

程序使用布尔值来选择执行哪个代码分支。 第 6章“C控制语句:循环”和 第 7章“C控制语句:分支和跳转”将详细介绍代码的执行,我们将在那里做进一步讨论、

3.4.5 可移植的类型 :inttypes.h

还有更多的整数类型吗?没有了,但是已有类型有一些别名。你可以认为自己已经接触到了足够多的名字,可是这些基本的名字不够明确。比如,知道一个变量是 int 类型并不能告诉你它有多少位,除非你查看系统文档。为解决这类问题, C99提供了一个可选的名字集合,以确切地描述有关信息。例如:int16_t 表示一个 16位有符号整数类型,uint32_t表示一个 32位无符号整数类型。

要使这些名字对于程序有效,应当在程序中包含 inttypes.h 头文件(注意,在编写本书第五版的时候,有些编译器还不支持这个特性)。这个文件使用 typedef 工具创建了新的类型名字 (第5章“运算符,表达式和语句”中有简要介绍)。比如,该头文件会用 uint32_t作为一个具有某种特征的标准类型的同义词或别名,在某个系统中这个标准类型可能是 unsigned int ,而在另一个系统中则可能是 unsigned long。编译器会提供同所在系统相一致的头文件。这些新的名称叫作“确切长度类型”(exact width type)。注意, 与 int 不同,uint32_t不是关键字,所以必须在程序中包含 innttypes.h 头文件,编译器才能识别它。

使用确切长度类型的一个潜在问题是某个系统可能不技能一些选择。比如,不能保证某个系统上存在一种 int8_t类型(8位有符号号数)。为了解决这个问题,C99标准定义了第2组名字集合。这些名字保证所表示的类型至少大于指定长度的最小类型,被称为“最小长度类型”(minimum width type)。例如,int_least8_t 是可以容纳 8位有符号数的那些类型中长度最小的一个的别名。某个特殊系统的最小类型的长度也许是 8位,而该系统上不一定会定义 int8_t类型。但是仍然可以使用 int_ieast8_t类型,它的实现也许是 16位整数。

当然,一些程序员更加关心速度而非空间。C99为他们定义了一组可使计算达到最快的类型集体。这组集合被称为“最快最小长度类型”(fastest minimum width type)。例如,把 int_fast8_t定义为系统中对 8位有符号数而言计算最快的整数类型的别名。

最后,对于某些程序员有时会需要系统最大的可能整数类型。为此,C99 把 intmax_t定义为最大的有符号整数类型,即可以容纳任何有效的有整数值的类型;把 uintmax_t定义为最大的无符号整数类型。顺便说一句,这些类型可能大于 long long 和 unsigned long 类型,因为除了要求实现的类型之外, C实现还可以定义其他类型.

C99 不仅提供这些新的,可移植的类型名,还提供了对这些类型数据进行输入输出的方法。例如,
printf()打印某类型的值时要求与之相对应的说明符。那么如果打印 int32_t类型值在一种定义中应使用 %d说明符,而在另一种定义中应使用 %ld说明符, 你该怎么办? C99标准提供了一些串宏来帮助打印这些可移植类型,详见第 4章。例如,inttypes.h 头文件将定义串 PRId16来表示打印 16位有符号值所需的合适说明符 (例如,hd 或 d)。程序清单 3.6演示了使用一种可类型及其相应说明符的方法。

------略 C++ 2010 VC 2005 都还不支持这种头文件

3.4.6 float, double, long double 类型

多数软件开发项目使用各种整数类型就可以工作得很好了。然而,账务和数学计算程序经常使用的是浮点数。 C语言中浮点数包括 float, double,和 long double类型,它们对应于 FORTRAN 和 Pascal语言中的 real类型。我们已经提到过,浮点方法能够表示包括小数在内的更大范围的数。浮点数表示类似于科学记数法。

科学家们使用科学记数法表示很大和很小的数,这种记数用十进制小数和 10 的幂的乘积来表示数字。表 3.3 是一些记数法的例子。

表 3.3 一些记数记的例子

数字 科学记数法 指数记数法
1 000 000 000 = 1.0 x 10^9 =1.0e9
123 000 = 1.23 x 10^5 =1.23e5
322.56 =3.2256 x 10^2 =3.2256e2
0.000 056 =5.6 x 10^-5 =5.6e-5

第一列是一般的记数法,第二列是科学记数法,第三列是指数记数法(或称为e-记数法),即科学记数法在计算机中的书写方式,其中 e 后面的是 10的指数。

C标准规定,float类型必须至少能表示 6位有效数字,取值范围至少为 10^-37 到 10^+37。 6位有效数字指浮点数至少应能精确表示像33.333 333这样数字的前 6位。取值范围的这一规定使你可以方便地表示诸如太阳的质量 (2.0e30 千克),质子的电量(1.6e-19库仑)以及国家债务之类的数字。通常,系统使用 32位存储一个浮点数。其中 8位用于表示指数及其符号, 24位用于表示非指数的部分(称为尾数或有效数字)及其符号。

C 还提供一种称为 double (竟为双精度)的浮点类型。double类型和 float类型具有在同的最小取值范围,但它必须至少能表示 10位有效数字。一般地,double使用64位而不是32位长度。一些系统将多出的32位全部用于尾数部分,这增加了数值的精度并减少了舍入误差。其他的一些系统将其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可以表示的数的范围。每种分配方法都使得数值至少具有13位有效数字,超出了 C 的最小标准规定。

C 提供了第三种浮点类型 long double 类型,以满足比 double类型更高的需求。不过,C 只保证 long double 类型至少同 double 类型一样精确

一·声明浮点变量

浮点变量的声明以及初始化方法同整形变量相同,下面是一些例子:

float noah,jonah;
double trouble;
float planck =6.63e-34
long doble gnp;

二·浮点常量

书写浮点常量有很多种选择。一个浮点常量最基本的形式是:包含小数点的一个带符号的数字序列,接着是字母 e或E,然后是代表 10的指数的一个有符号值。下面是两个有效的浮点常量:

-1.56E+12
2.87e-3

可以省略正号。可以没有上数点 (2E5)或指数部分 (19.28),但不能同时没有二者。可以省略纯小数部分(3.E16)或整数部分(.45E-6),但二者不能同时省略。下面是更多的有效的浮点常量:

3.14159
.2
4e16
.8E-5
100.

在浮点常量中不要使用空格。

错误 1.56 E+12

默认情况下,编译器将浮点常量当作 double类型。例如,假设 some是一个 float变量,你有下面的语句:

some = 4.0 * 2.0;

那么 4.0 和 2.0被存储为 double类型,(通常)使用 64位进行存储。乘积运算使用双精度,结果被截为正常的 float长度。这能保证计算精度,但是会减慢程序的执行。

C使你可以通过 f 或 F 后缀使编译把浮点常量当作 float类型,比如 2.3f和 9.11E9F。l或L后缀使一个数字成为 long double类型,比如 54.31 和 4.32e4L。建议使用 L后缀,因为字母 l 和数字 1容易混淆。没有后缀的浮点常量为 double类型。

C99为表示浮点常量新添加了一种十六进制格式。这种格式使用前缀 0x或0X,接着是十六进制数字,然后是 p 或 P (而不是 e或 E),最后是 2的指数(而不是 10的指数)如下所示:

0xa.1fp10

a 是10,1f表示 1/16 加上 15/256,P10表示 2^10(即1024)。整个数的十进制值为10364.0。

并非所有的 C 编译器都添加了对这 C99特性的支持。

三·打印浮点值

printf()函数使用 %f 格式说明符打印十进制记数的 float 和 double数字,用 %e打印指数记数法的数字。如果系统技能 C99的十六进制格式浮点数,你可以使用 a或A 代替 e或E。打印 long double类型需要 %Lf,%La说明符。注意 float 和double类型的输出都使用 %f ,%e 或 %a 说明符。这里由于当它们向那些末在原型中显式说明参数类型的函数 (如printf())传递参数时, C自动将
float类型的参数转换为 double类型,程序清单3.7了这一特性。

#include <stdio.h>
int main (void)
{
float aboat = 32000.0;
double abet = 2.14e9;
long double dip = 5.32e-5;
printf ("%f can be written %e\n",aboat,aboat);
printf ("%f can be written %e\n",abet,abet);
printf ("%f can be written %e\n",dip,dip);
getchar();
return 0;
}

注: long double dip = 5.32e-5; VC2005 和 BCB2010 的输出结果不一样 vc2005 和本教程的输出结果一致。

四 浮点值的上溢和下溢

假设系统中最大的 float值为 3.4E38 并如下操作:

float toobig = 3.4e38 * 100.0f;
print ("%e\n",toobig);

会发生什么? 这是一个上溢(overflow)的例子。当计算结果是一个大得不能表达的数时,会发生上溢。对这种情况的反应原来没有锁定,但是现在的 c语言要求为 toobig赋予一个代表无穷大的特殊值,printf()函数显示此值为 inf 或 infinity (或这个含义的其他名称)。

当除以一个十分小的数时,情况更复杂一些。回忆一下,float数字被分为指数和尾数部分进行存储。有这样的一个数,它具有最小的指数,并且仍具有可以由全部可用位进行表示的最小的尾数值。这将是能用对浮点值可用的全部精度进行表示的最小数字。现在把此数除以 2.通常这个操作将使指数部分减小,但是指数已经达到了最小值,所以计算机只好将尾数部分的位进行右移,空出首位二进制位,并丢弃最后一位二进制值,以十进制为例,把一个包含四位有效数字的数 0.1234e-10 除以 10,将得到结果 0.0123e-10,但是损失了一位有效数字。此过程称为下溢(underflow)。C将损失了类型精度的浮点值称为低于正常的 (subnormal),所以把最小的正浮点除以 2将得到一个低于正常的值。如果除以一个足够大的值,将使所有的位都为 0.现在 C库提供了用于检查计算是否会产生低于正常的值的函数。

还有另外一个特殊的浮点值 NaN(Not-a-number)。例如 asin()函数返回反正弦值,但是正弦值不能大于 1,所以 asin()函数的输入参数不能大于1,否则函数返回 NaN值,printf()函数将此值显示为 nan,NaN或类似形式。

PS: 将一个数加上1 再减去原数,结果为1 。如果使用浮点计算,则可能会有其他结果,如下便所示
#include <stdio.h>
int main (void)
{
float a,b;

b = 2.0e20 + 1.0;
a = b -2.0e20;
printf ("%f \n",a);
getchar();
return 0;
}

VC++ 2005 能执行 BCB 2010 一执行 就出错 要重装

出现这种奇怪的结果是由于计算机缺乏足够的进行正确运算所需的十进制位数。数字 2.0e20 为2后面加20个零,如果对它加1,那么变化的是第21位。如果要正确计算,至少需要存储21位的数字,而 float数字只有6.7位有效数字。因此这个计算注定是不正确的。另一个方面若使用 2.0e4代替 2.0e20,则能得到正确的结果,因为改变的是第 5位数字, float数字对此足够精确。

3.4.7 复数和虚数类型

良多科学和工程计算需要复数和虚数。C99标准支持这些类型,但是有所保留 。一些*实现中不需要这些类型,比如 一些嵌入式处理器的实现 (VCR就不需要复数)。同样的,虚数类型也是可选的。

简单地讲,有3种复数类型,分别是 float_Complex,double_Complex 和 long double_Complex.
float_Complex 变量包含两个float值,一个表示复数的实部,另一个表示复数的虚部。与之类似,有3种虚数类型,分别是 float_Imaginary,, double_imainary, long double_Imainary.

如果你包含了 complex.h头文件,则你可以用 complex代替 _Complex , imaginqry 代替 _Imaginary 用符号 I 表示 -1的平方根。

3.4.8 其他类型

现在已介绍了所有的基本数据类型。对于有些人,类型可能太多,另一些人可以认为还需要更多的类型。C 没有字符串类型,但是它仍然可以很好地处理字符串,详细内容请参见第 4章。

C 从基本类型中衍生出其他类型,包括数组,指针,结构和联合。尽管我们在后面章节中才会介绍指针类型,本章已经在示例中使用(指针(pointer)指向变量或其他数据对象的位置,scanf()函数中就使用 &前缀创建一个指向信息存储位置的指针)。

PS: 总结 : 基本数据类型
----------------------------------------------------------------
关键字:

基本数据类型使用 11个关键字: int,long,short,unsigned,char,float,double,signed, _Bool, _Complex,和 _Imaginary .

----------------------------------------------------------------
有符号整数:

这种类型可以取正值及负值。

int : 系统的基本整数类型。 C保证 int 类型至少有 16位长。

short 或 long int :最大的 short整数不大于最大的 int整数值。 C保证 short类型至少有
16 位长 。

long 或 long int :这种类型的整数不小于最大的 int整数值,C保证 long至少有32位长

long long 或 long long int :这种类型的整数不小于最大的 long整数值。long long类型
至少是 64位长。

一般地,long类型长于 short类型,int类型和它们基本的一个长度相同。例如,PC机上基于 DOS的系统提供 16位长的 short 和 int类型,以及 32位长的 long类型,而基于 Windows 95 的系统提供 16位长的 short 以及 32位长的 int类型和 long类型。

如果你喜欢,可以使用 signed 关键字修饰任何一种有符号,以明确表示这一属性。

---------------------------------------------------------------------

无符号整数:

无符号整数只有 0 和正值,这使得无符号数可以表达比有符号数更大的正值。使用 unsigned关键字表示无符号数,例如:unsigned int,unsigned long 和 unsigned short。单独的 unsigned 等价于 unsigned int 类型。

--------------------------------------------------------------------

字符:

字符包括印刷字符,如 A,&和 +。在定义中,char类型使用 1个字节的存储空间表示一个字符。出于历史原因,字符字节通常为 8位,但出于表示基本字符集的需要,它也可以为 16位或者更长。

char : 字符类型的关键字。一些实现使用有符号的 char,另外一些则使用无符号 char。
C 允许使用 signed 和 unsigned 关键字标志 char 的符号属性。

---------------------------------------------------------------------

布尔值:

布尔值表示 true 和 false, C使用 1 代表 true, 0 代表 false。

_Bool : 此类型的关键字。布尔值是一个无符号整数,其存储只需要能够表示 0 和 1的空间。

---------------------------------------------------------------------

实浮点数:

float : 系统的基本浮点类型。至少能精确表示 6位有效数字。

double; 范围(可能)更大的浮点类型。能表示比 float类型更多的有效数字 (至少 10位通常会 更多)以及更大的指数。

--------------------------------------------------------------------

复数和虚浮点数:

虚数类型是可选的类型,实部和虚部基于台下相应的实数类型:

float _Complex
double _Complex
long double _Complex
float _Imaginary
double _Imaginary
long double _Imaginary

PS:

总结:如如何声明简单变量

---------------------------------------------------------------------
1.选择所需类型。

2.选用合法的字符为变量起一个名字

3.使用下面的声明语句格式

type - specifier variable -name;

type - specifer 由一个或多个类型关键字组成,下面是一些声明的例子:

int erest;
unsigned short cash; 。

4.可以在同一类型后声明多个变量,这些变量名之间用逗号分隔,如下例所示:

char ch,init,ans;

5.可以在声明语句中初始化变量,如下例所示:

float = 6.0E24;

--------------------------------------------------------------------------------

3.5 使用数据类型

开发程序时,应当注意所需变量及其类型的选择。一般地,使用 int 或 float类型表示数字,使用 char类型表示字符。在使用变量的函数开始处声明该,并为它选择有意义的名字。初始化变量使用的常量应当同变量类型相匹配。例如:

int apples =3; //正确
int oranges =3.0 //不好的形式

与pascal语言相比,C语言对待类型不匹配现象更宽容。C编译器允许二次初始化,但是会给出警告,是在你激活了较高级别警告的时候,最好不要养成这样粗心的习惯。

当为某个数值类型的变量进行初始化时,如果使用了其他类型的值,C会自动对该值进行类型转换以便和变量类型相匹配,这意味着可能会丢失一部分数据。例如,考虑下列初始化语句:

int cost = 12.99 //把一个int 变量初始化为一个 double值
float pi = 3.1415926536 // 把一个 float变量初始化为一个 double值

第一个声明把 12赋予 cost。在将浮点值转换为整数时,C简单地丢弃小数部分(截性),而不进行四舍五入。第二个声明会损失部分精度,因为 float类型只能保证前 6位是精确的。编译器可能会对这样的初始化语句产生警告。但这并不是它必须做的。如果进行这样的初始化,编译程序清单 3.1中的程序是地你可能已遇到了这种警告。

很多程序员和组织都有系统化的变量命名规则,其中变量的名字可以表示它的类型。例如:使用i_前缀表示 int变量,使用 us_表示 unsigned short 变量。这样通过名字就可以确定变量 i_smart 为 int类型,变量 us_verysmart 为 unsigned short 类型。

----------------------------------------------------------------------------------

3.6 参数和易犯的错误

有必要重复并深入介绍一下本章前面担到的关于 printf()的使用。传递给函数的信息被称为参数。例如,函数调用 printf(“Hello,pal。”)包含一个参数 “Hello,pal。”用双引起来的一串字符称为字符串,详见第4章。现在要指出的是,不论包含多少字符和标点符号,一个字符串只是一个参数。

与之类似,函数调用 scanf (“%d”,&weight)包含两个参数:“%d”和 &weight。C用逗号来隔开函数调用中的多个参数。printf()和scanf()函数比较特殊,其参数数目可以不受限制。例如,我们曾经使用 1个,2个,甚至 3个参数调用 printf()函数。程序需要知道参数的数目才能正常工作。这两个函数通过第一个参数确定后续参数的个数,方法是第一个参数字符串中的每个说明符对应了后面的一个参数。例如下面的语句包含两个格式说明符:%d 和 %d。

printf ("%d cats ate %d cans of tuna\n",cats,cans);

这告诉程序后面还有 2个参数,确实有 2个:cats 和 cans。

程序员要保证格式说明符的数目同后面的参数数目相同。现在 C通过一种函数原型机制检查函数调用是否使用了正确数目及类型的参数,但是这对printf()和scanf()函数不起作用,因为它们的参数数目是变化的。如果参数数目存在问题,会出现什么情况?例如,假设你编写了程序清单 3.9中的程序。

程序清单 3.9 badcount.c程序
------------------------------------------------------------------------
/* badcount.c --- 不正确的参数个数 */
#include <stdio.h>
int main (void)
{
int f = 4;
int g = 5;
float h = 5.0f;

printf ("%d\n",f,g); // 参数太多
printf ("%d %d \n",f); //参数太少
printf ("%d %f\n",h,g); //值的类型不正确
return 0;
}

注意,使用 %d显示 float值不会把该 float值转换为近似的 int值,而是显示垃圾值,与之类似,使用 %f显示int值也不会把该值转换为浮点值。而且,参数的数目不足和类型不匹配所造成的结果也将随平台不同而不同。

我们所尝试的编译器都没有对上面的代码提出异议,在运行程序时也不会报告错误。没错,有的编译器可能会捕捉到这种错误,但是 C标准并没有要求它们这么做。因此,计算机在运行时可能不捕捉这种类型的错误。由于程序运行正常,所以你也很难觉察这样的错误。如果程序没有显示期望的值或显示了异常的值,则应当检查 printf()函数参数个数是否正确。顺便说下,UNIX语法检查程序 ling 比 UNIX编译更为严格,它会检查出 printf ()的参数错误。

3.7 另一个例子: 转义序列

下面是另一个打印程序,它使用了 C的一些专用转义字符。程序清单 3.10演示了退格(\b),制表符(\t)和回车符(\r)的工作方式。这些概念从计算机使用电传打字机作为输出设备时就开始使用,但它们并不一定能成功地与现代图形接口兼容。比如,此程序不能在某些 Macitosh实现中正确运行

---------------------------------------------------------------------------
程序清单 3.10 escape.c 程序

/* escape.c ---- 使用转义字符 */
#include <stdio.h>
int main (void)
{
float salary;
printf ("dEnter your desired monthly salary:"); // 1
printf ("$______\b\b\b\b\b\b\b\b"); // 2
scanf ("%f", &salary);
printf ("\n \t$%.2f a month is $%.2f ayear.",salary,salary * 12.0);
printf ("\r Gee!\n");
return 0;

}

---------------------------------------------------------------------

3.7.2 刷新输出

printf()函数什么时候真正把输出传送给屏幕?首先,printf()语句将输出传递给一个被称为缓冲区(buffer)的中介存储区域。缓冲区中的内容再不断地被传递给屏幕。标准 C规定在以下几种情况下将缓冲区内容传给屏幕:缓冲区满的时候,遇到换行符的时候以及需要输入的时候。将缓冲区内容传送给屏幕或文件称为刷新缓冲区(flushing the buffer)。例如,上例中,前两个 printf()语句既没有缓冲区也不包含换行符,但是后面紧跟一个 scanf()语句要求输入。迫使printf()输出内容被传给屏幕。

你可能会遇到早期的 C语言版本,这样的版本中遇到 scanf()语句不强迫缓冲区刷新,这将使程序停在那里等待你的输入,而没有显示任何提示信息。为停止此问题,可以用换行符刷新缓冲区,如下所示:

printf ("enter your desired monthly salary;\n");
scanf("%f",&sclary);

不管后续的输入语句是否引起刷新缓冲区,该代码都会正常工作。但是,这样做使光标移到下一行起始位置,防止你在提示字符串的同一行输入数据。另一个解决办法是使用 fflush()函数,详见第12章“文件输入/输出”。

3.8 关键概念

C 包含大量数值类型,这体现了为程序员提供方便 C的设计这一意图。比如对于整数,C并不认为一种整数已经足够,而是努力给程序员以多种选择(有符号和无符号),以最好的数值范围满足某个具体程序的需求。

计算机中,浮点数和整数有很大不同,它们的存储和运算都有很大区别。两个 32位存储单元的每个位状态都相同,但是如果把一个解释为 float类型,另一个解释为 long类型,它们将表示完全没有关系的两个值,例如,在 PC机中,一个存储单元表示 float数,值为 256.0;如果把它解释为 long数,则其值为 113246208 。C允许混合数据类型的表达式,但它会自动进行类型转换,以使实际的计算只使用一种类型。

计算机内存中用数值编码来表示字符。美国最常用的数值编码是 ASCII码,C也支持其他编码的使用。字符常量是计算机系统所使用的数值编码的符号表示,它表示为单引号中的一个字符(如‘A’)。

3.9 总结

C 有多种数据类型。基本的数据类型包含两大类:整数类型和浮点类型。整数类型的两个重要特征是其类型的大小以及它是有符号还是无符号的。最小的整数类型是 char(字符实际也是整数类型的一种),因实现不同可以是有符号或无符号的,可以使用 signed char 和 unsigned char 确定该类型的符号属性,不过这通常用于使用此类型表示小整数而非字符编码。其他的整数类型包括 short,int,long 和 long long 类型。对于上述类型的大小,C要求后面的类型不能小于前面的类型。上述类型都是有符号的,但可以使用 unsigned 关键字产生相应的无符号类型:unsigned short,unsigned int,
unsigned long 和 unsigned long long 类型,也可以使用 signed修饰词明确地表示一个类型为有符号类型。最后,_Bool类型是一种无符号类型,它只包含两个值 0 和 1,对应于 false 和 true 。

3种浮点类型为 float,double 和 ANSI C新增的 long double,后面类型的大小至少要和前面的类型一样大。有些实现中支持复数和虚数类型,方法是把 _Complex 和 _Imaginary 关键字同浮点类型字结合使用,例如 double_Complex 和 float_Imaginary 类型。

整数可以表达为十进制,八进制或十六进制形式。前缀 0 指示八进制数,前缀 0x 或 0X指示十六进制数。例如,32,040 和 0x20分别表示十进制,八进制和十六进制的同一个值。后缀 l 或 L指示 long 类型值,后缀 ll 或 LL 表示 long long 类型值。

字符常量表示为放在单引号中的一个字符,比如 ‘Q’,‘8’和‘$’。C的转义序列(例如‘\n’)用于表示一些非打印字符。可以使用诸如‘\007’这样的形式通过字符的 ASCII 码表示一个字符。

浮点数可以书写为小数点固定的形式,比如 9393.912;或者书写为指数形式,比如 7.38E10 。

printf()函数通过对应于各种类型的转换说明符打印相应类型的数据。形式最简单的转换说明符由一个百分号和一个指示类型的字符组成,比如 %d 或 %f。

3.10 复习题

------------------------------------------------------
1.对下面的各种数据使用合适的数据类型:
a East Simpleton 的人口
b DVD影碟的价格
c 本章出现次数最多的字母
d 这个字母出现的次数

答: a int类型, 可以是 short,unsigned 或 unsigned short; 人口数是一个整数。
b float 价格不太可能正好是一个整数(也可以用 double 但是实际上并不需要那么高的精度)
C char 类型
d int 类型。可以是 unsigned

------------------------------------------------------

2. 需要 long类型变量代替 int类型变量的原因是什么?

答:一个原因是你的系统中 long可以容纳比 int更大的数,另一个原因是确实需要处理更大的值,
那么使用在所有系统上都保证至少是 32位的类型会使程序的移植性更好。

-----------------------------------------------------------

3. 获得一个 32位的有符号整数,可以使用哪些可移植数据类型?每种选择的原因是什么?

答:要获得正好是 32位的数,你可以使用 int32_t(如果在你的系统中有这一定义的话)。要获得
存储 32位的最小类型,可以使用 int_least32_t。如果要在 32位的类型中获得提供最快计算速度
类型,可以选择 int_fast32_t。

-----------------------------------------------------------

4. 指出下列常量的类型和意义 (如果有的话)
a. '\b'
b. 1066
c. 99.44
d. 0xAA
e. 2.0e30

答:
a. char常量 (但以 int类型存储)
b. int常量
c. float常量
b. unsigned int 常量, 十六进制格式
e. double常量

------------------------------------------------------------

5. Dottie Cawm 写的下面这个程序中有很多错误,找出这些错误。

intclude <stdio.h>
main
{
float g;h;
float tax,rate;

g = e21;
tax = rate*g;
}

答:
第1行: 应该是 #include <stdio.h>
第2行: 应该是 int main (void)
第3行: 使得 {, 而不是 (。
第4行: 在 g 和 h之间应该有逗号而不是分号。
第5行: 无错误
第6行: 空行 (无错误)
第7行: 有 e之前应该至少有一个数字,尽管这个数有点大。
第8行: 无错误,至少在语法上没有。
第9行: 使用 }, 而不是 )。

缺少的行:首先,rate没有被赋值。其次,就是 h 从来没有被使用。而且程序永远不会把它的计算结果通知给你。这些错误都不会阻止程序的运行(尽管可能会和你出示一个警告以说明变量没有被使用),但是它们确实减弱了程序本来就不多的功能。而且在结尾处应该有一个return 语句。

应该像这样:
# include <stdio.h>
int main (void)
{
float g,h;
float tax,rate;

rate = 0.08;
g = 1.0e5;
tax = rate * g;
h = g + tax;
printf ("You owe $%f plus $%f in taxes for a total of $%f.\n",g,tax,h);
return 0;
}

--------------------------------------------------------------------------

6. 指出下表中各常量的数据类型(在声明语句中使用的数据类型)及其在 printf()中的格式说明

常量 类型 说明符
a 12 int %d
----------------------------------------------
b 0x3 unsigned int %#X
----------------------------------------------
c 'c' char %c
----------------------------------------------
d 2.34e07 double %e
----------------------------------------------
e '\040' char %c
----------------------------------------------
f 7.0 double %f
----------------------------------------------
g 6L long %ld
----------------------------------------------
h 6.0 float %f
----------------------------------------------

------------------------------------------------------------------------------

7. 指出下表中各常量的数据类型(在声明语句中使用的数据类型)及其在 printf()中的格式说明符
假设 int 类型为16位长。

常量 类型 说明符
-----------------------------------------
a 012 unsigned int %#o
-----------------------------------------
b 2.9e05L long double %Le
-----------------------------------------
c 's' char %c
-----------------------------------------
d 10000 long %ld
-----------------------------------------
e 'n' char %c
-----------------------------------------
f 20.0f float %f
-----------------------------------------
g 0x44 unsigned int %x
-----------------------------------------
-----------------------------------------------------------------------------------

8. 假设一个程序开始处有如下声明:

int imate = 2;
long shot = 53456;
char grade = 'A';
flooat log = 2.71828;

在下面 printf ()语句中添上合适的类型说明符

printf (" The odds ageinst the %d were %ld to 1,\n",imate shot);
printf (" A score of %f is not an %c gtade.\n",log,grade);

------------------------------------------------------------------------------------

9. 假设 ch 为 char类型变量。使用转义序列,十进制值,八进制字符常量以及十六进制字符常量等方法将其赋值为回车符 (假设使用 ASCII 编码值)。

ch = '\r';
ch = 13;
ch = '\015'
cd = '\xd'
-------------------------------------------------------------------------------------

3.11 编程练习

------------------------------------------------------------------------
1.通过试验的方法(即编写带有此类问题的程序)观察系统如何处理整数上溢,浮点数上溢和浮点为数下溢的情况。

答:

#include <stdio.h>
int main (void)
{
int intt;
intt = 2147483648;

printf ("这是int类型的最大值 %d,将它加1后得 %d, 将它加2后得 %d \n",
intt, intt+1,intt+2);

float overflow;
float overflow1;
overflow = 0.1234567;
overflow1 = overflow * 100e100;
printf (" overflow 的值为 %f 但结果上溢后 显示 %f \n", overflow,overflow1);

float underflow,underflow1;
underflow = 0.1234567;
underflow1 = underflow / 10;
printf (" underflow 的值为 %f 下溢后的情况 %f \n", underflow,underflow1);

getchar();
return 0;
}

--------------------------------------------------------------------------------------
2. 编写一个程序,要求输入一个 ASCII 码值 (如66),然后输出相应的字符

答:

#include <stdio.h>
int main (void)
{

char ch;
printf ("请输入一个数字,结果将是转换成 ASCII 的字母\n");

scanf ("%d", &ch);
printf ("你输入的数字为 %d,对应 ASCII的字母是 %c \n",ch,ch);
getchar();
getchar();
return 0;
}

注意:注意说明符的使用 数字应为 %d 字符要为 %c
------------------------------------------------------------------------------------------

3. 编写一个程序,发出警报声,并打印下列文字:

Startled by the sudden sound,Sally shouted,"By the Great pumpkin,what was that!"

答:

#include <stdio.h>
int main (void)
{
printf ( "\a\a\a\a\a\a\a\a\a");
printf ("Startled by the sudden sound,Sally shouted,\n ""By the Great pumpkin,what was that!" );

getchar();
return 0;
}
------------------------------------------------------------------------------------------

4. 编写一个程序,读入一个浮点数,并分别以小数形式和指数形式打印。输出应如同下面的格式

The imput is 21.290000 or 2.2129000e+001.

答:

#include <stdio.h>
int main (void)
{
float num;
scanf ("%f",&num);
printf (" The imput is %f or %e ",num,num);
getchar();
getchar();
return 0;
}

------------------------------------------------------------------------------------------

5. 一年约有 3.156 X 10^7s(s为秒的单位)。编写一个程序,要求输入你的年龄,然后显示该年龄合多少秒

答:

#include <stdio.h>
int main (void)
{
int age;
printf ("请输入你的年龄 ");
scanf ("%d",&age);
float s = 3.156e7;
printf ("你的年龄为 %d 指数形式为 %e 浮点形式为 %f ",age,s*age,s*age);

getchar();
getchar();
return 0;

}
------------------------------------------------------------------------------------------
6. 1个水分子的质量约为 3.0 X 10^-23 g,1夸脱水大约有 950g 。编写一个程序,要求输入水的夸脱数,然后显示这么多水中包含多少个水分子。单位

答;

#include <stdio.h>
int main (void)
{
int quart;
printf (" 请输入夸脱数 ");
scanf ("%d",&quart);

float unit = 3.0 * 10E-23;
printf (" 你输入的夸脱为 %d,里面有水分子 (指数形式 %e 浮点形式 %f)",quart,quart/unit,quart/unit);

getchar();
getchar();
return 0;

}

注意 : 常量最好是直接赋值 而不是像变量那里赋给 如:float unit = 3.0 * 10E-23;
--------------------------------------------------------------------------------------

7. 1英寸等 2.54cm。编写一个程序,要求输入你的身高 (以英寸为单位),然后显示该身高值等于多少厘米。如果你愿意,也可以要求以厘米为单位输入身高,然后以英寸为单位进行显示。

答:

#include <stdio.h>
int main (void)
{

int stature;
printf(" 请输入你的身高");
scanf("%d",&stature);
float cm = 2.54;
printf("你的身高为 %d 英寸,%f 厘米", stature,stature*cm);
getchar();
getchar();
return 0;

}

注意:默认的 %f 说明为数字后还带4个零, 要想显示小数点后多少位的格式为 %.nf (n即为多少位 比如:要显示小数点后二位 %.2f)
------------------------------------------------------------------------------------------

上一篇:Sqlserver到处数据到Excel


下一篇:机器学习技法-AdaBoost元算法