c primer 第三章 B

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)
---------------------------------------------------

posted @ 2017-05-08 19:00  sheldon_lee  阅读(445)  评论(0编辑  收藏  举报