第三章

数据和C

3.1 变量与常量数据

变量

  • 在程序运行期间可能会改变或被赋值。这些称为变量(variable)

常量

  • 在程序运行期间没有改变的值。这些被称为常量(constant)

3.2 数据:数据类型关键字

最初K&R给出的关键字C90标准添加的关键字C99标准添加的关键字
intsigned_Bool
longvoid_Complex
short_Imaginary
unsigned
char
float
double
  • C中用int关键字来表示基本的整数类型。后3个关键字(long , shortunsigned)和C90新增的signed用于提供基本整数类型的变式,如unsigned short intlong long int
  • char关键字用于指定字母和其他字符(如,#,$,%和*)。此外,char类型也可以表示较小的整数
  • floatdoublelong double表示带小数点的数
  • _Bool类型表示布尔值true/false)。
  • _Complex表示复数
  • _Imaginary表示虚数
  • 按照计算机的存储方式可以分为两大基本类型整数类型浮点数类型

字节是描述计算机数据单元或存储单元的术语。主要指存储单元。

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

字节byte)是常用的计算机存储单位。对于几乎所有的机器,1字节均为8位。既然1位可以表示0或1,那么8位字节就有256(2的8次方)种可能的0,1组合。通过二进制编码(仅用0和1便可以表示数字),便可表示0~255的整数或一组字符。

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

3.2.1 整数

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

在这里插入图片描述

3.2.2 浮点数

  • 在一个值后面加上一个小数点,该值就成为一个浮点值。如7是整数,7.00是浮点数。
  • 浮点数的书写方式
    • e计数法:如 3.16 E 7 3.16E7 3.16E7表示 3.16 ∗ 1 0 7 3.16*10^7 3.16107

3.2.3 浮点数与整数的存储方式

  • 浮点数

    • 计算机将浮点数分成小数部分和指数部分来表示,并将两部分分开存储。(因此导致虽然7和7.00在数值上相同,但是他们的存储方式不同)
    • 在十进制下,可以把 7.0 7.0 7.0写成 0.7 E 1 0.7E1 0.7E1。0.7是小数部分,1是指数部分。(计算机内部使用二进制)
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述
  • 两种类型区别

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

3.3 基本数据类型

3.3.1 int类型

  • C中整数类型可以表示不同的取值范围和正负值。一般情况下使用int类型即可。
  • int类型是有符号整型,即int类型的值必须是整数,可以是正整数,负整数或零。
  • ISO C规定int的取值范围最小为 − 32768 到 32767 -32768到32767 3276832767。不少于16位。
1.声明int变量
  • 声明单个变量:int xxx;
  • 声明多个变量:int xxx,yyy,zzz;
2.初始化变量
  • 初始化(initialize)变量就是为变量赋一个初始值。

  • 赋值方式

    • int hogs = 21;
      
    • int cows = 32, goats = 14;
      
3.int类型常量
  • 上面的整数(21,32,14)都是整型常量和整型字面量。C中把不含小数点和指数的数作为整数。把大多数整型常量视为int类型,但是非常大的整数除外。
4.八进制与十六进制
  • 十六进制数的每一位的数恰好由4位二进制数表示。

    • 如:十六进制3的二进制数是0011。
    • 十六进制数5的二进制是0101。
    • 十六进制数35的位组合(bit pattern)是 00110101 00110101 00110101

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

    • 十六进制53的位组合是 01010011 01010011 01010011
  • C中用特定的前缀表示使用哪种进制。

    • 0x0X前缀表示十六进制值。如十进制16表示成0x10或0X10。
    • 0前缀表示八进制。如十六进制数16表示成八进制是020。
5.显示八进制和十六进制
进制符号
十进制%d
八进制%o
十六进制%x
进制前缀符号
八进制前缀 0%#o
十六进制前缀 0x%#x
十六进制前缀 0X%#X
  • //
    // Created by ym on 2021/2/22.
    //
    /*
     * 输出10进制,8进制,16进制
     */
    #include <stdio.h>
    int main(void) {
        int x = 100;
        printf("x十进制为 %d \n 八进制为 %o \n 十六进制为 %x \n",x,x,x);
        printf("x十进制为 %d \n 八进制为 %#o \n 十六进制为 %#x,%#X \n",x,x,x,x);
        return 0;
    }
    
    
  • output

  • x十进制为 100
     八进制为 144
     十六进制为 64
    x十进制为 100
     八进制为 0144
     十六进制为 0x64,0X64
    

3.3.2 其他整数类型

  • C中提供了3个附属关键字修饰基本整数类型:shortlongunsigned
    • short int(简写为short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。与int类似,short是有符号类型(正负)类型至少占16位。
    • long intlong占用的存储空间可能比int多,适用于较大数值的场合。与int类似,long是有符号类型。至少占32位。
    • long long intlong long(C99标准加入)占用的存储空间可能比long多,适用于更大数值的场合。该类型至少占64位。与int类似,long long是有符号类型。
    • unsigned intunsigned只用于非负值的场合。这种类型与有符号类型表示的范围不同。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。
    • C90中,添加了unsigned long intunsigned longunsigned short intunsigned short类型。C99中添加了unsigned long long intunsigned long long
    • 在任何有符号类型前面添加关键字signed,可强调使用有符号类型的意图。如:short,short int,signed short,signed short int都表示同一种类型。
1. 使用多种整数类型的原因
  • 为什么说short类型“可能”比int类型占用的空间少,long类型“可能”比int类型占用的空间多?
    • 因为C语言只规定了short占用的存储空间不能多于int,long占用的存储空间不能少于int。这样的规定是为了适应不同的机器。
  • 现在,个人计算机上最常见的设置是,long long占64位,long占32位,short占16位,int占16或32位(依计算机的自然字长而定)。
  • C对基本数据类型只规定了允许的最小大小。
    • 对于16位机,short和int的最小取值范围是[ − 2 15 -2^{15} 215 2 15 − 1 2^{15}-1 2151]。
    • 对于32位机,long的最小取值范围是[ − 2 31 -2^{31} 231, 2 31 − 1 2^{31}-1 2311]。
    • 对于32位机,unsigned short和unsigned int的最小取值范围是[ 0 0 0, 65535 65535 65535]。
    • 对于32位机,unsigned long的最小取值范围是[ 0 0 0, 2 32 − 1 2^{32}-1 2321]。
    • long long是为了支持64位的需求,最小取值范围为[ − 2 63 -2^{63} 263, 2 63 − 1 2^{63}-1 2631]。
    • unsigned long long的最小取值范围是[ 0 0 0, 2 64 − 1 2^{64}-1 2641]。
2. 如何选择适合的int类型
  • 首先,考虑unsigned类型。这种类型的数常用于计数,因为计数不用负数。而且,unsigned类型可以表示更大的正数。

  • 如果一个数超出了int范围,但在long类型范围内,使用long类型。然而对于那些,long占用的空间比int大的系统,使用long类型会减慢运行速度。

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

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

    • 通常,只有当程序使用相对于系统可用内存较大的整型数组时,才需要重点考虑节省空间的问题。
3. long常量和long long常量
  • 超出int类型能表示的范围,编译器会将其视为long int类型(假设这种类型可以表示该数字)。如果数字超出long可表示的最大值,编译器则将其视为unsigned long类型。如果还不够大,编译器则将其视为long long或unsigned long long类型(前提是编译器能识别这些类型)。

  • 八进制和十六进制常量被视为int类型。如果值太大,编译器会尝试使用unsigned int。如果还不够大,编译器会依次使用long,unsigned long,long long和unsigned long long类型。

  • 有些情况下,需要编译器以long类型存储一个小数字。此外,一些C标准函数也要求使用long类型的值。要把一个较小的常量作为long类型对待,可以在值的末尾加上l(小写L)或L后缀。

  • 在支持long long类型的系统中,也可以使用ll或LL后缀表示long long类型的值,如3LL。另外,u或U后缀表示unsigned long long,如5ull,10LLU。

整数溢出
  • 如果整数超出了响应类型的取值范围会怎样?分别将有符号类型和无符号类型的整数设置为比最大值略大。

  • #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);
        return 0;
    }
    
  • output

  • 2147483647 -2147483648 -2147483647
    4294967295 0 1
    
  • 可以将无符号整数j看作是汽车的里程表,当达到它能表示的最大值时,会重新从起始点开始。整数i也是类似的情况。它们的主要区别是,在达到最大值时,unsigned int类型的变量j从0开始;而int类型的变量i则从-2147483648。

4. 打印short,long,long long和unsigned类型
  • 打印unsigned int类型的值使用%u转换说明。

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

  • 如果系统中int和long的大小相同,使用%d就行。

  • 这样的程序被移植到其他系统(int和long类型的大小不同)中会无法正常工作。在x和o前面可以使用l前缀,%lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。(虽然C允许使用大写或小写的常量后缀,但是在转换说明中只能使用小写)

  • 打印short类型,可以使用h前缀,%hd表示以十进制显示short类型的整数。%ho表示以八进制显示short类型的整数。h和l前缀都可以和u一起使用,用于表示无符号类型。如:%lu表示打印unsigned long类型的值。

  • 对于支持long long类型的系统,%lld和%llu分别表示有符号和无符号类型。

  • #include <stdio.h>
    int main(void) {
        unsigned int un = 3000000000;
        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);
        return 0;
    }
    /**
    *程序输出
    * un = 3000000000 and not -1294967296
    * end = 200 and 200
    * big = 65537 and not 1
    * verybig = 12345678908642 and not 1942899938
    */
    
  • 上面程序第3行中,把65537以二进制格式写成一个32位数是00000000000000010000000000000001。使用%hd,printf()只会查看后16位,所以显示的值是1。类似,输出的最后一行先显示verybig的完整值,然后由于使用了%ld,printf()只显示存储在后32位的值。

3.4.3 使用字符:char类型

  • char类型用于存储字符(如:字母或标点符号),但是从技术层面看,char是整数类型,因为char类型实际上存储的是整数而不是字符。(如:在ASCII码中,整数65即代表大写字母A)。
  • 标准ASCII码的范围是0~127,只需要7位二进制数(64,32,16,8,4,2,1)即可表示。通常char类型被定义为8位的存储单元。
  • 商用的统一码(Unicode)创建了一个能表示世界范围内多种字符集的系统,目前包括的字符已超过110000个。国际标准化组织(ISO)和国际电工技术委员会(IEC)为字符集开发了ISO/IEC 10646标准。统一码标准也与ISO/IEC 10646标准兼容。
  • C语言把1字节定义为char类型占用的位(bit)数,因此无论是16位还是32位系统,都可以使用char类型。
1. 声明char类型
  • char response;
    char itable, latan;
    
  • 上面声明创建了3个char类型的变量;response,itable和latan。

2. 字符常量和初始化
  • 在C中用单引号括起来的单个字符被称为字符常量(character constant)

  • char grade = 'A';
    char broiled;				/*声明一个char类型的变量*/
    broiled = 'T';				/*为其赋值,正确*/
    broiled = T;				/*错误,此时T是一个变量*/
    broiled = "T"				/*错误,此时“T”是一个字符串*/
    
  • 实际上,字符是以数值形式存储的,所以也可以使用数字代码来赋值。

    • char grade = 65;
      
    • 不建议使用这种方式,能这样做的前提是系统使用ASCII码。

  • C将字符常量视为int类型而非char类型。如。在int为32位,char为8位的ASCII系统中,有如下代码:

    • char grade = 'B';
      
    • 本来’B’对应的数值66存储在32位的存储单元中,现在却可以存储在8位的存储单元中(grade)。利用字符常量的这种特性,可以定义一个字符常量’FATE’,即把4个独立的8位ASCII码存储在一个32位存储单元中。如果把这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的值是‘E’。

    • #include <stdio.h>
      int main(void) {
          char grade = 'FATE';
          printf("%c",grade);
          return 0;
      }
      
3. 非打印字符
  • 有些ASCII字符打印不出来,如,一些代表行为的字符(如:退格,换行,终端响铃或蜂鸣等)。C提供了3种方法表示这些字符。

    • 1.使用ASCII码,如:蜂鸣字符的ASCII码是7,因此表示为

      • char beep = 7;
        
    • 2.使用特殊的符号序列表示一些特殊的字符,这些符号叫做转义序列(escape sequence),把转义序列给字符变量时,必须用单引号把转义序列括起来。如

      • char nerf = '\n';
        
    • 3.用16进制表示字符常量,即反斜杠后面跟一个x或X,再加上1~3位16进制数字。

      • 类型十六进制八进制十进制
        char\x41\101N.A.
        int0x41010165
        unsigned int0x41u0101u65u
        long0x41L0101L65L
        unsigned long0x41UL0101UL65UL
        long long0x41LL0101LL65LL
        unsigned long long0x41ULL0101ULL65ULL
    • 使用ASCII码时,注意数字和数字字符的区别,如:字符4对应的ASCII码是52,'4’表示字符4,而不是数值4。

4. 打印字符
  • printf()函数用%c指明待打印的字符。如果用%d转换说明打印char类型变量的值,答应的是一个整数。而%c转换说明告诉printf()打印该整数值对应的字符。

  • //显示字符的代码编号
    
    #include <stdio.h>
    int main(void) {
        char ch;
        printf("Please enter a character: ");
        scanf("%c",&ch);            //用户输入字符
        printf("The code for %c is %d. \n",ch,ch);
        return 0;
    }
    
  • output

  • Please enter a character:Q
     The code for Q is 81.
    
  • printf()函数中的转换说明决定了数据的显示方式,而不是数据的存储方式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

5. 有符号还是无符号
  • 有些C编译器把char实现为有符号类型,这表示char表示的范围是-128~127。而有些编译器把char实现为无符号类型,那么char可表示的范围是0-255。
  • 根据C90标准,C语言允许在关键字char前使用signed或unsigned。

3.4.4 _Bool类型

  • C99中添加了Bool类型,用于表示布尔类型,因为C语言用值1表示true,值0表示false,所以 Bool类型实际上也是一种整数类型。但原则上它仅占用1位存储空间。

3.4.5 可移植类型:stdint.h和inttypes.h

  • C中提供了很多有用的整数类型,但是,某些类型名在不同系统中的功能不一样。C99新增了两个头文件stdint.hinttypes.h,以确保C语言的类型在各系统中的功能相同。

  • C中为现有类型创建了更多类型名。新的类型名定义在stdint.h头文件中。

    • 如:int32_t表示32位的有符号整数类型。在使用32位int的系统中,头文件会把int32_t作为int的别名。不同的系统也可以定义相同的类型名。
    • 如:int16位long32位的系统会把int32_t作为long的别名。然后,使用int32_t类型编写程序,并包含stdint.h头文件时,编译器会把intlong替换成与当前系统匹配的类型。
  • 上面说的类型别名是精确宽度整数类型exact-width integer type)。int32_t表示整数类型的宽度正好是32位,但是计算机的底层系统可能不支持。

  • 如果系统不支持精确宽度整数类型咋办?C99C11提供了第2类别名集合。称为最小宽度类型minimum width type)。

    • 如:int_least8_t是可容纳8位有符号整数值的类型中宽度最小的类型的一个别名。
  • C99C11定义了一组可使计算达到最快的类型集合。这组类型集合被称为最快最小宽度类型(fastst minimum width type)。

    • 如:int_fast8_t被定义为系统中对8位有符号值而言运算最快的整数类型的别名。
  • C99定义了最大的有符号整数类型intmax_t,可存储任何有效的有符号整数值。uintmax_t表示最大的无符号整数类型。这些类型可能比long longunsigned long类型更大,因为C编译器除了实现标准规定的类型外,还可利用C语言实现其他类型。

  • C99C11提供了输入和输出,如果要打印int32_t类型的值,有些定义使用**%d**,而有些定义使用**%ld**,C标准针对这一情况,提供了一些字符串宏来显示可移植类型。

  • 如:inttypes.h头文件中定义了PRId32字符串宏,代表打印32位有符号值的合适转换说明(如d或l)。

  • //可移植整数类型名
    
    #include <stdio.h>
    #include <inttypes.h>       //支持可移植类型
    int main(void) {
        int32_t me32;           //me32是一个32位有符号整型变量
        me32 = 706540874;
        printf("First, assume int32_t is int: ");
        printf("me32 = %d\n",me32);
        printf("Next, let's not make any assumptions.\n");
        printf("Instead, use a \"macro\" from inttypes.h: ");
        printf("me32 = %" PRId32 "\n",me32);
        
        return 0;
        /**
         * output
         * First, assume int32_t is int: me32 = 706540874
         * Next, let's not make any assumptions.
         * Instead, use a "macro" from inttypes.h: me32 = 706540874
         */
    }
    

3.4.6 float,dobule和long double

  • C中的浮点类型有float,doublelong double类型。浮点类型能表示包括小数在内更大范围的数。
  • float类型必须至少能表示6位有效数字,且取值范围至少是 1 0 − 37 10^{-37} 1037~ 1 0 + 37 10^{+37} 10+37。通常,系统存储一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下的24位用于表示非指数部分(也称为尾数或有效数)及其符号。
  • double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。一些系统将多出的32位全部用来表示非指数部分,这不仅能提高精度,还减少了舍入误差。一些系统把其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可表示数的范围。无论采用哪种方式,double的值至少有13位有效数字,超过了标准的最低位数规定。
  • long double用以满足比double类型更高的精度要求,C只保证long double类型至少与double类型的精度相同。
1. 声明浮点型变量
float noah,jonah;
double trouble;
float planck = 3.14e-12;
long double gnp;
2. 浮点型常量
  • 浮点型常量的基本形式是:有符号的数字(包括小数点),后面紧跟e或E,最后是一个有符号数表示10的指数。
    • 正号可以省略。
    • 可以没有小数点或指数部分。不能同时省略两者。
    • 可以小数部分或整数部分。不能同时省略两者。
  • 在浮点数后面加上f或F后缀可覆盖默认类型(double为浮点型默认类型),编译器会将浮点型常量看作float类型。使用l或L后缀使得数字成为long double类型。没有后缀的浮点型常量是double类型。

C99添加了一种新的浮点型常量格式——使用十六进制表示浮点型常量,即在十六进制数前加上十六进制前缀(0x或0X),用p或P分别代替e或E,用2的幂代替10的幂(即,p计数法)。

如:0xa.1fp10

十六进制a等于十进制10,.1f是1/16加上15/256(十六进制f等于十进制15),p10是 2 10 2^{10} 210或1024。0xa.afp10表示的值是(10 + 1/16 + 15/256)x 1024(即,十进制10364.0)。

注:并非所有的编译器都支持C99这一特性

3. 打印浮点值
  • printf()使用%f转换说明打印十进制计数法的floatdouble类型浮点值,用**%e打印指数计数法的浮点数。如果系统支持十六进制格式的浮点数,可用aA分别代替eE**。打印long double类型要使用**%Lf**,%Le或**%La**转换说明。

  • //以两种方式显示float类型的值
    
    #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);
        //下一行要求编译器支持C99或其中的相关特性
        printf("And it's %a in hexadecimal, powers of 2 notation\n",aboat);
        printf("%f can be written %e\n",abet,abet);
        printf("%Lf can be written %Le\n",dip,dip);
        return 0;
    }
    
    /**
     * output
     * 32000.000000 can be written 3.200000e+04
     * And it's 0x1.f4p+14 in hexadecimal, powers of 2 notation
     * 2140000000.000000 can be written 2.140000e+09
     * 0.000053 can be written 5.320000e-05
     */
    

3.4.7 复数和虚数类型

  • C99标准支持复数类型和虚数类型,但是有所保留。C11标准把整个复数软件包都作为可选项。
  • C有3种复数类型
    • float_Complex
    • double_Complex
    • long double_Complex
  • C有3种虚数类型
    • float_Imaginary
    • double_Imaginary
    • long double_Imaginary
  • 如果包含complex.h头文件,便可以用complex代替**_Complex,用imaginary代替_Imagimary**,还可以用I代替**-1**的平方根。
3.4.8 类型大小
//
// Created by ym on 2021/2/27.
//查看当前系统支持的指定类型的大小

#include <stdio.h>
int main(void) {
    printf("int %zd bytes.\n", sizeof(int));
    printf("char %zd bytes.\n", sizeof(char));
    printf("short %zd bytes.\n", sizeof(short));
    printf("long %zd bytes.\n", sizeof(long));
    printf("long long %zd bytes.\n", sizeof(long long));
    printf("double %zd bytes.\n", sizeof(double));
    printf("long double %zd bytes.\n", sizeof(long double));
    printf("float %zd bytes.\n", sizeof(float));
    return 0;
}
/**
 * output
 * int 4 bytes.
 * char 1 bytes.
 * short 2 bytes.
 * long 4 bytes.
 * long long 8 bytes.
 * double 8 bytes.
 * long double 12 bytes.
 * float 4 bytes.
 */
  • sizeof是c内置运算符,以字节为单位给出指定类型的大小。C99C11提供**%zd转换说明匹配sizeof的返回类型。一些不支持C99C11的编译器可用%u%lu代替%zd**。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值