C 语言学习 第二章 数据和 C

 本章涉及了 scanf() 和 printf() 函数,将会在下章详细讲解,您在看本章时可以先猜测其用法。并且,标题前打上 * 号是可读内容,因为其在后续学习中基本不会重新提到,之后一样。同时,本人在写每一章时都会附上思维导图,但可能会与本章一些内容有所不同,请不要过分依赖本人所写的思维导图。还有在上一章中 “使用 C 语言的  7 个步骤” 和 “调试” 很重要,这些可能会在您编码时经常用到。如果遗忘,请回去在看一遍。上章入口:【C 语言学习 第一章 概论】

  • 1. 常量和变量

  •        顾名思义,有些数据可以在程序使用之前预先设定并在整个运行过程中没有变化,这称常量。另外的数据在程序运行过程中可能变化或被赋值,这称变量。
           尽管这对于您来说可能是非常简单的常识,但这些还是需要您记住,因为它很重要。
  •        变量的命名必须遵循以下几条规则:
           1)必须是以英文字母或下划线开头的,由字母、数字和下划线组成的字符序列。例如,max_number、NameOfFile、_day、group3 等都是合法的变量名;而 my-name、Smonth 则属于非法的变量名。
           2)不能与C语言的关键字(保留字)重名,因为关键字已经被赋予了特殊的含义。
           3)变量名的长度不受限制。
           4)C 语言对变量名的大小写敏感,如 Max 和 max 就是不同的变量名。
           另外,在C语言的长期使用过程中还形成了一些约定俗成的规则:
           1)尽量使变量名能够表达出该变量的含义,如year、max 等,最好不要使用a、、SpS等类似无意义的变量名,因力这将降低程序的可读性。
           2)C语言系统内部已经定义了一些以下划线开头的标识符,为了避免冲突和以示区别,用户最好不要用下划线来作为变量名的开头。
           3)习惯上符号常量的标识符用大写字母,变量标识符可大小写结合。
           在程序中出现的变量都必须先说明再使用,变量的说明也叫作变量的定义。变量数据类型的说明格式为:
                                    <数据类型> 变量名[=初值];
           例如:int max,min:
                      float number ;
                      char FirstWord ;
    也可以在说明变量的同时给变量赋值,如 int year=2004,month=9。
     
  • 2. 数据类型

    • 2.1 C 的数据类型

      表 1  C 的数据关键字
      原来的 K&R 关键字intlongshortunsignedcharfloatdouble
      C90 关键字signedvoid
      C99 关键字_Bool_Complex_Imaginary
      • 2.1.1 整数类型

      • 整数就是没有小数部分的数。
      • 常见类型
        • 常量

        • (1) 十进制整型常量
                 十进制整型常量没有前缀,其数码 0~9。
          以下各数都是合法的十进制整常数:56、 100、2004。
          以下各数都不是合法的十进制盤带数:023(不能有前导。).23D(含有非十进制数码)。
          (2)八进制整型常量
                  八进制繁型常量必须以 0 开头,即以 0 作为八进制数的前缀,数码取值为0~7。八进制数通常是无符号数。
          以下各数都是合法的八进制数:017(十进制为 15 )、0101(十进制为 65)。
          以下各数都不是合法的八进制数:17(无前缀 0 )、082(包含了非八进制数码)。
          (3)十六进制整型常量
                 十六进制整型常量的前缀 0X 或 0x,其数码取值为 0~9 、A~F 或 a~f 。
                 以下各数都是合法的十六进制整常数:0X2A(十进制为 42 )、0xA0(十进制为 160 )、OXFFFF(十进制为 65535 )。
                  以下各数都不是合法的十六进制整常数:5A(无前缀0X)、0X3H(含有非十六进制数码)。
          (4)整型常量的后缀
                 如果使用的数超过了整型数的范围,就必须用长整型数来表示。长整型数是用后缀“L”或“l”来表示的。如果是一个无符号整型常量,则在整数值后面加上“U”或“u”对于无符号的长整型常量的表示方法是在整数后面加上“UL”,"LU" ,"ul" 或 "lu"。
                  例如:158L(十进制为158)、OXA5L(十进制为165)、358U、0x38Au均为无符号数。
                  前缀、后缀可同时使用以表示各种类型的数,如 OXA5LU 表示十六进制无符号长整型 A5 ,其十进制数为 165 。

        • 变量

        • int a;
          long b=0;
          signed int c;
          unsigned long int d;
                 在 C 语言中,整型用 int 表示。根据整型数在存储器中占用的字节数,又可以用 long 和 short 来修饰 int ,表示长整型和短整型;根据其是否带有符号,又可以用 signed 和 unsigned 来修饰 int ,表示带符号整型和无符号整型。当整型带有修饰语时,可将 int 省略,如 short int 可以写为 short , unsigned longint 可以写为 unsigned long 。带有不同修饰语的整型具有不同的字节数及数据范围。
                 各种无符号类型量所占的内存空间字节数与相应的有符号类型量相同。但由于省去了符号位,故不能表示负数,数据范围也不同。表2-2中列出了BC31 编译系统中各类整型量所分配的内存字节数及数的表示范围。
          例如:有符号短整型变量表示的最大正数为 32 767:

          无符号短整型变量表示的最大正数为 65 535:

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

      • *可移植类型:inttypes.h
                还有更多的整数类型吗?没有了,但是已有类型有一些别名。您可能认为自己已经接触到了足够多的名字,可是这些基本的名字不够明确。比如,知道一个变量是 int 类型并不能告诉您它有多少位,除非您查看系统文档。为解决这类问题,C99提供了一个可选的名字集合,以确切地描述有关信息。例如:int16_t表示一个 16 位有符号整数类型,uint32_1表示一个32位无符号整数类型。
                要使这些名字对于程序有效,应当在程序中包含 inttypes.h 头文件(注意,有些编译器还不支持这个特性)。这个文件使用 typedef 工具创建了新的类型名字(之后会讲)。比如,该头文件会用 uint32_t 作为一个具有某种特征的标准类型的同义词或别名,在某个系统中这个标准类型可能是 unsigned int,而在另一个系统中则可能是unsigned long。编译器会提供同所在系统相一致的头文件。这些新的名称叫作“确切长度类型”(exactwidth type)。注意,与 int  不同,uint32_t 不是关键字,所以必须在程序中包含 inttypes.h 头文件,编译器才能够识别它。
                使用确切长度类型的一个潜在问题是某个系统可能不支持一些选择。比如,不能保证某个系统上存在一种 int8_t 类型(8位有符号整数)。为解决这个问题,C99标准定义了第 2 组名字集合。这些名字保证所表示的类型至少大于指定长度的最小类型,被称为“最小长度类型”(minimum width type)。例如,int_least8_t是可以容纳8位有符号数的那些类型中长度最小的一个的别名。某个特殊系统的最小类型的长度也许是8位,而该系统上不一定会定义 int8_t 类型。但是仍然可以使用 int_fast8_t 类型,它的实现也许是16位整数。
               当然,一些程序员更加关心速度而非空间。C99为他们定义了一组可使计算达到最快的类型集合。这组集合被称 "最快最小长度类型" (fastest minimum width type  )。例如,把 int_fast8_1 定义为系统中对8位有符号数而言计算最快的整数类型的别名。
               最后,对于某些程序员有时会需要系统最大的可能整数类型。为此,C99把intmax_t 定义为最大的有符号警数类型,即可以容纳任何有效的有符号整数值的类型:类似地,把 uintmax_t 定义为最大的无符号整数类型。顺便说一句,这些类型可能大于 long long 和 unsigned long 类型,因为除了要求实现的类型之外,C实现还可以定义其他类型。
               C99 不仅提供这些新的、可移植的类型名,还提供了对这些类型数据进行输入输出的方法。例如,printf()打印某类型的值时要求与之相对应的说明符。那么如果打印 int32_t类型值在一种定义中应使用%d说明符,而在另一种定义中应使用%ld说明符,您该怎么办?C99标准提供了一些串宏来帮助打印这些可移植类型。
         

      • 2.1.2 浮点数类型

        • 常量

        •         浮点型也称为实型,浮点型常量也称为浮点数或实数。在C语言中,浮点型常量只来味十进制表示,它有两种形式:十进制小数形式和指数形式。
                (1) 十进制小数形式
                  由数字0~9和小数点组成(注意必须有小数点)。
                  例如:0.0、3.14、300.、-267.8230 等均为合法的实数。
                (2) 指数形式
                  由符号(+或一)、整数部分、小数点(.)、小数部分、指数部分(e或E\pmn)和浮点数后缀组成。其一般形式为:
                  [\pm][整数部分][.][小数部分][e/E\pmn][后缀]
          其中,“整数部分.小数部分”一般称为尾数;e\pmn(或E\pmn)表示尾数乘上10的正或负 n 次方,n 称为阶码。n 为 1~3 位十进制无符号整常数,可以有前置 0,但并不代表八进制整常数。例如,一1.23456e+4或一1.23456e+04均表示浮点数 -12345.6。
          后级表示浮点数的类型,浮点数后缀可以是 f 或 F、l 或 L 。当后缀省略时,浮点数类型为 double;当后缀为 f 或 F 时,浮点数类型为 float ;当后缀为 l 或 L时,浮点数类型为long double。
                 符号 “[]” 表示该组成部分可有可无(可选项)、但必须遵守浮点數的下列组成规则:
                 1)一个浮点数可以无整数部分或小数部分,但不能二者全无。
                 2)一个浮点数可以无小数点或指数部分,但不能二者全无。
                 例如,以下是一些合法的浮点数:
                 .234e +12(无整数部分) 值等于 2.34*10^{11}
                  1.             (无小数部分) 值等于 1.0
                  25E5        (无小数点)    值等于 2.5*10^{6}
                  1.23         (无指数部分) 值等于1.23
                  +1.23e-4f (全有)            值等于1.23*10^{-4}(float 类型)
                  1.5e+31L (全有)            值等于1.5*10^{31}   (long double 类型)
                  以下不是合法的浮点数:
                  345             无小数点
                  -E7             阶码标志 E 之前既无整数部分也无小数部分
                 100. -E3      负号位置不对
                 -5                 无阶码标志也无小数点
                  2.7E            无阶码
                  注意:123. 和 2. 是浮点数;而 123 和 2 则为整数,而不是浮点数。

        • 变量

          float a=0.1;
          double b=1.0;
          long double c=1.1e-1;

        •        C 语言中提供了两种浮点数的类型:
                 float         单精度
                 double     双精度
                 它们之间的区别在于,在 BC31 中,float 型数据占 4 字节,double 型数据占 8 字节。同时它们表示的数据验围和精度也不同。与整型数不同,浮点数均有符号浮点数,不能用 signed 和 unsigned 修饰。但是,符号 long 可以用来修饰 double 形成 long double 类型(长双精度型)。BC31中,各类型浮点型数据所表示的数的范围以及有效位数见下表:

          表 2  BC31 中数据类型的长度和值域
          类型名说明数的范围字节数
          int整型-32 768~32 7672
          signed int有符号整型-32 768~32 7672
          unsigned int无符号整型0~65 5352
          short int短整型-32 768~32 7672
          signed short int有符号短整型-32 768~32 7672
          unsigned short int无符号短整型0~65 5352
          long int长整型-2 147 483 648~2 147 483 6474
          signed long int有符号长整型-2 147 483 648~2 147 483 6474
          unsigned long int无符号长整型0~4 293 967 2954
          float单精度浮点型\pm|3.4*10^{-38}|\pm |3.4*10^{38}| 有效数位 7 位4
          double双精度浮点型\pm |1.7*10^{-308}| ~\pm |1.7*10^{308}|  有效数位 15 位8

          long double

          长双精度浮点型\pm |3.4*10^{-4932}| ~ \pm |3.4*10^{4932}| 有效数位 18 位10
          char字符型-128~1271
          signed char有符号字符型-128~1271
          unsigned char无符号字符型0~2551

          (这里需要说明一下,这些数据与您使用的 C 语言的环境有一定关系,或许有些值不符合,可以使用关键字 sizeof 来计算出大小。)

      • 2.1.3 char 类型

        • 常量

        •         字符型常量是用单引号括起来的一个字符。一个字符常量在计算机的內存中占据 1 字节,字符常量的值就是该字符的 ASCII 码值。因此,1 字节常量实际上也是 1 字节的整型常量,可以参与各种运算。
          例如:'a'、'C'、'='、'?' 都是合法字符常量。但是,单引号中的内容不能是单引号、双引号和反斜线。
          例如:' ' '、' '' '、' \ '(这是为了让您更好看清才加的空格,实际上该展示的是不加空格的,下同理。)都是不合法的。这是因为单引号、双引号和反斜线具有其他的特殊用途。如果需要表示它们,正确的写法是 ' \' '、' \'' ' 、' \\ '。
                 转义字符是一种特的字符常量。转义字符以反斜线 “\” 开头,后跟一个或几个字符。转义字符具有特定的含义,不同于字符原有的意义,故称 “转义” 字符。例如,在前面各例题 printf 函数的格式串中用到的 '\n' 就是一个转义字符,其意义是“回车换行”。转义字符主要用来表示那些用一般字符不便于表示的控制代码。
                 常用的转义字符如下表所示。广义地讲,C语言字符集中的任何一个字符均可用转义字符来表示。表中的 “ddd” 和 "xhh” 正是为此而提出的。ddd 和 hh 分别为八进制和十六进制的 ASCII 代码。如 “\101” 表示字母 A,"\102'' 表示字母B,"\134" 表示反斜线,''IX0A" 表示换行等。

          表 3  常用的转义字符及其含义
          转义字符转义字符的意义ASCII 代码
          \n      回车换行10
          \t      横向跳到下一制表位置9
          \b      退格8
          \r      回车13
          \f      走纸换页12
          \\      反斜线符(\)92
          \'      单引号符(')39
          \''      双引号('')34
          \a      鸣铃7
          \0      空字符(=NULL)0
          \ddd      1~3位八进制数所代表的字符
          \xhh      1~2位十六进制数所代表的字符

          (至于 ASCII 码表放在文章最下方)

        • 变量

          char a='0';
          char b=10;

                 字符变量用来存储字符常量,即单个字符。字符变量的类型说明符是 char 。在 C 语言中,字符型也可以分为 signed 和 unsigned ,它们的区别在于取值范围的不同、有关规定见表2。每个字符变量被分配一字节的内存空间、因此只能存放一个字符。字符值是以 ASCII 码的形式存放在变量的内存单元中的。
          例如,x 的十进制 ASCII码是 120, y 的十进制 ASCII码是121。对字符型变量a、b 赋予值 'x' 和 'y':

          char a='x';//等同于 char a=120;
          char b='y';//等同于 char b=121;

          所以,也可以把字符变量看成是整型量。C语言允许对整型变量赋以字符值,也允许对字符变量赋以整型值。在输出时,允许把字符变量按整型量输出,也允许把整型量按字符变量输出。由于整型量为多字节量,字符变量为单字节量,当整型量按字符型变量处理时,只有低 8 位数据参与处理。

      • *2.1.4 复数和虚数类型

      •        很多科学和工程计算需要复数和虚数。C99 标准支持这些类型,但是有所保留。一些自由实现中不需要这些类型,比如一些嵌入式处理器的实现(VCR 芯片就不需要复数)。同样的,虚数类型也是可选的。简单地讲,有 3 种复数类型,分别是  float_Complex、double_Complex 和 long double_Complexe float_Complex 变量包含两个 float 值,一个表示复数的实部,另一个表示复数的虚部。与之类似,有3种虚数类型,分别是 float_Imaginary、double _Imaginary 和 long double_Imaginary。
               如果您包含了 complex.h 头文件则您可以用 complex 代替_Complex,用  imaginary 代替_Imaginary,用符号 I 表示 -1 的平方根。
      • 2.1.5 其他类型`

      •        现在已经介绍了所有的基本数据类型。对于有些人,类型可能太多,另一些人可能认为还需要更多的类型。C 没有字符串类型,但是它仍然可以很好地处理字符串。
                C 从基本类型中衍生出其他类型,包括数组、指针、结构和联合。尽管我们在后面章节中才会介绍指针类型,本章已经在示例中使用指针(指针(pointer)指向变量或其他数据对象的位置,scanf()函数中就使用 & 前缀创建一个指向信息存储位置的指针)。
    • 2.2 使用数据类型

    •        开发程序时,应当注意所需变量及其类型的选择。一般地,使用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 位是精确的。编译器可能会对这样的初始化语句产生警告,但这并不是它必须做的。
             很多程序员和组织都有系统化的变量命名规则,其中变量的名字可以表示它的类型。例如:使用 i_ 前缀表示 int 变量,使用 us_ 表示 unsigned short 变量。这样通过名字就可以确定变量 i_smart 为 int 类型,变量 us_verysmart 为 unsigned short 类型。

    • 2.3 转义序列

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

        /* escape.c-- 使用转义字符*/
        #include ‹stdio.h›
        int main (void)
        {
          float salary:
          printf ("\aEnter your desired monthly salary: ");    /* 1 */
          printf (" $_______\b\b\b\b\b\b\b");                  /* 2 */
          scanf ("&f", &salary);
          printf ("\n\$%.2f a month is $%.2f a year.", salary,
                     salary  * 12.0);                          /* 3 */
          printf ("\rGee!\n");                                 /* 4 */
          return 0:
        }
      • 2.3.1 过程分析

      •         假设在 ANSI C环境中运行此程序,下面逐步研究此程序。第一条 printf()语句(标 1 的语句)发出一声警告声音(由 \a 产生),并打印出下列内容:
                Enter your desired monthly salary:
                由于字符串结尾没有\n,所以光标仍然停留在冒号后边。
                第2条 printf()语句紧接着前面打印,则屏幕显示如下:
                Enter your desired monthly salary: $_______
                在冒号和美元符号之间有一个空格,因为第 2 条 printf()语句中的字符串以空格开始。7 个退格字符使光标左移 7 个位置,也就是把光标向左移动过那 7 个下划线字符,使它直接紧跟在美元符的后面。通常,退格字符不删除退回时所经过的字符,但有些实现是删除的,这会和本练习有所不同。
               这时,键入回答 2000.00,则屏幕显示为:
               Enter your desired monthly salary: $2000.00
               键入的字符代替了下划线字符。按下 Enter(或 Retum)键以发出您的回答,光标将移到下一行的起始位置。
                第 3 条 printf()语句以nit开始。换行符使光标移到下一行的起始位置,制表符使光标移到该行中的下一个制表点,一般是第 9 列(但不一定)。然后打印字符串其余的部分。此语句执行完毕时,屏幕显示如下:
                Enter your desired monthly salary: $2000.00
                             $2000.00 a month is $24000.00 a year.
                由于 printf()语句没有使用换行符,所以光标停留在句号后面。
                第4条 printf()语句以 \r 开始,使光标移至当前行起始位置,然后显示Gee!,接着 \n 使光标移到下一行的起始位置。屏幕上最后结果为:
                Enter your desired monthly salary: $2000.00
                Gee!      $2000.00 a month is $24000.00 a year.
        ( 加粗代表输入,后续一样。)

      • 2.3.2 刷新输出

      •        printf ()函数什么时候真正把输出传送给屏幕?首先,printf()语句将输出传递给一个被称为缓冲区 (buffer)的中介存储区域。缓冲区中的内容再不断地被传递给屏幕。标准C规定在以下几种情况下将缓冲区内容传给屏幕:缓冲区满的时候、遇到换行符的时候以及需要输入的时候。将缓冲区内容传送给屏幕或文件称为刷新缓冲区((flushing the buffer)。例如,上例中,前两个 printf()语句既没有填满缓冲区也不包含换行符,但是后面紧跟了一个 scanf()语句要求输入。迫使 printf()的输出内容被传给屏幕。
               您可能会遇到早期的 C 语言版本,这样的版本中遇到 scanf()语句不强迫缓冲区刷新,这将使程序停在那里等待您的输入,而没有显示任何提示信息。为防止此闷题,可以用换行符刷新缓冲区,如下所示:
                printf ("Enter your desired monthly salary: \n") ;
                scanf ("%f", &salary) ;
                不管后续的输入语句是否引起刷新缓冲区,该代码都会正常工作。但是,这样做使光标移到下一行起始位置,防止您在提示字符申的同一行输入数据。另一个解决办法是使用 fflush()函数,这个之后会讲。

 参考书籍:《C Primer Plus》【美】 Stephen Prata 著

                         《程序设计教程 用 C/C++ 语言编程》 周纯杰 何顶新 周凯波 彭刚 张惕远 编著

ASCII 码对照表:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值