C和指针 第3章 数据 3.1 基本数据类型

数据
    程序对数据进行操作。描述数据的各种类型、特点以及如何声明它。本章还将描述变量的3个属性---作用域、链接属性和存储类型。这3个属性决定了一个变量的“可视性”(也就是它可以在什么地方使用)和“生命期”(它的值将保持多久)。
3.1 基本数据类型
    整数家族包括字符、短整型、整型和长整型,它们都分为有符号(signed)和无符号(unsigned)两种版本。
    规定整型值相互之间大小的规则很简单:
    长整型至少应该和整型一样长,而整型至少应该和短整型一样长。
    K&R C:
    ANSI C标准加入了一个规范,说明了各种整型值的最小允许范围。当各个环境间的可移植文件非常重要时,这个规范较之K&R C就是一个巨大的进步,尤其是在那些机器的系统结构差别极大的环境里。
                        变量的最小范围
    类型                                        最小范围
    char                                         0~127
    signed char                             -127~127
    unsigned char                         0~255
    short int                                   -32767~32767
    unsigned int                            0~65535
    int                                            -32676~32767
    unsigned int                            0~65535
    long int                                    -2147483647~2147483647
    unsigned long int                    0~4294967295
    short int至少是16位,long int至少是32位。至于缺省的int究竟是16位还是32位,或者其他值则由编译器设计者决定。通常这个选择的缺省值是这种机器最为自然(高效)的位数。同时你还应该注意到标准也没有规定这3个值必须不一样。
    头文件limits.h说明了各种不同的整数类型的特点。limits.h同时定义了下列名字:CHAR_BIT是字符型的位数(至少8位);CHAR_MIN和CHAR_MAX定义了缺省字符类型的范围,它们或者应该与SCHAR_MIN和SCHAR_MAX相同,或者应该与0和UCHAR_MAX相同;最后,MB_LEN_MAX规定了一个多字节字符最多允许的字符数量。
                        变量范围的限制
                       |            signed                                        |    unsigned
        类型        |    最小值                   最大值                  |    最大值
        字符        |    SCHAR_MIN         SCHAR_MAX      |    UCHAR_MAX
        短整型    |    SHRT_MIN            SHRT_MAX         |    USHRT_MAX
        整型        |    INT_MIN                INT_MAX            |    UINT_MAX
        长整型    |    LONG_MIN            LONG_MAX        |    ULONG_MAX
    尽管设计char类型变量的目的是为了让它们容纳字符型值,但字符在本质上是小整型值。缺省的char要么是signed char,要么是unsigned char,这取决于编译器。这个事实意味着不同机器上的char可能拥有不同范围的值。所以,只有当程序所使用的char型变量的值位于signed char和unsigned char的交集中时,这个程序才是可移植的。例如,ASCII字符集中的字符都是位于这个范围之内的。
    在一个把字符当做小整型值的程序中,如果显式地把这类变量声明为signed或unsigned,可以提高这类程序的可移植性。这类做法确保不同的机器中在字符是否为有符号值方面保持一致。另一方面,有些机器在处理signed char时得心应手,如果硬把它改成unsigned char,效率可能受损。所以把所有的char变量统一声明为signed或unsigned未必是上上之策。同样,许多处理字符的库函数把它们的参数声明为char,如果你把参数显式声明为unsigned char或signed char,可能会带来兼容性问题。
    提示:
    当可移植问题比较重要时,字符是否为有符号数就会带来两难的境地。最佳妥协方案就是把存储于char型变量的值限制在signed char和unsigned char的交集内,这可以获得最大程度的可移植性,同时也不牺牲效率。并且,只有当char型变量显式声明为signed或unsigned时,才对其执行算术运算。
    1.整型字面值
    字面值(literal)这个术语是字面值常量的缩写---这是一种实体,指定了自身的值,并且不允许发生改变。这个特点非常重要,因为ANSI C允许命名常量(named constant,声明为const的变量)的创建,它与普通变量极为类似。区别在于,当它被初始化以后,它的值不能改变。
    literal这个词有时译为“字面值”,有时译为“常量”,它们的含义相同,只是表达的习惯不一。
    当一个程序内出现整型字面值时,它是属于整型家族9中不同类型中的哪一种呢?答案取决于字面值是如何书写的,但是你可以在有些字面值的后面添加一个后缀来改变缺省的规则。在整数字面值后面添加字符L或l(这是字母l,不是数字1),可以使这个整数被解释为long整型值,字符U或u则用于把数值指定为unsigned整型值。如果在一个字面值后面添加这两组字符中的各一个,那么它就被解释为unsigned long整型值。
    在源代码中,用于表示整型字面值的方法有很多,其中最自然的方式是十进制整型值,诸如:
        123 65535 -34
    从技术上说,-275并非字面值常量,而是常量表达式。负号被解释为单目操作符而不是数值的一部分。
    十进制整型字面值可能是int、long或unsigned long。在缺省情况下,它是最短类型但能完整容纳这个值。
    整数也可以用八进制表示,只要在数值前面以0开头。整数也可以用十六进制来表示,它以0x开头。例如:
        0173 0177777
        0X7b 0xFFFF
    在八进制字面值中,数字8和9是非法的。在十六进制中,可以使用字母ABCDEF或abcdef。八进制和十六进制字面值可能的类型是int、unsigned int、long或unsigned long。在缺省情况下,字面值的类型就是上述类型中最短但足以容纳整个值的类型。
    另外还有字符常量。它们的类型总是int。你不能在它们后面添加unsigned或long后缀。字符常量就是一个用单引号包围起来的单个字符(或字符转义序列或三字母词),诸如:
        'M' '\n'  '??(' '\377'
    标准也允许诸如'abc'这类的多字节字符常量,但它们的实现在不同的环境中可能不一样,所以不鼓励使用。
    最后,如果一个多字节字符常量的前面有一个L,那么它就是宽字符常量(wide character literal)。如:
        L'x'  L'e^'
    当运行时环境支持一个宽字符集时,就有可能使用它们。
    提示:
    采用何种整型字面值书写方式,应该取决于这个字面值使用时的上下文环境。
    当一个字面值用于确定一个字中某些特定位的位置时,将它写成十六进制或八进制值更为合适,因为这种写法更清晰地显示了这个值的特殊本质。如果在某种上下文环境中,这些特定的位非常重要,那么字面值写成十六进制形式可以使操作的含义对于读者而言更为清晰。
    如果一个值被当作字符使用,那么把这个值表示为字符常量可以使这个值的意思更为清晰。例如:
    value = value - 48;
    value = value - \60;
    和下面这条语句
    value = value - '0';
    的含义完全一样,但最后一条语句的含义更为清晰,它用于表示把一个字符转换为二进制值。更为重要的是,不管所采用的是何种字符集,使用字符常量所产生的总是正确的值,所以它能提高程序的可移植性。
    2.枚举类型
    枚举(enumerated)类型就是指它的值为符号常量而不是字面值的类型,它们以下面这种形式声明:
    enum Jar_Type {CUP,PINT,QUART,HALF_GALLON,GALLON};
    这条语句声明了一个类型,称为Jar_Type。这种类型的变量按下列方式声明:
    enum Jar_Type milk_jug, gas_can, medicine_bottle;
    如果某种类型的枚举类型的变量只使用一个声明,就可以把上面两条语句组合成下面的样子:
    enum {CUP,PINT,QUART,HALF_GALLON,GALLON} milk_jug, gas_can, medicine_bottle;
    这种类型的变量实际上以整型的方式存储,这些符号的实际值都是整型值。这里CUP是0,PINT是1,以此类推。适当的时候,可以为这些符号名指定特定的整型值,如下所示:
    enum Jar_Type {CUP = 8,PINT = 16,QUART = 32,HALF_GALLON = 64,GALLON = 128};
    只对部分符号名用这种方式进行赋值也是合法的。如果某个符号名并未显式指定一个值,那么它的值就比前一个符号名的值大1。
    提示:
    符号名被当做整型常量处理。声明为枚举类型的变量实际上是整数类型。但是,要避免以这种方式使用枚举,因为把枚举变量同整数无差别地混合在一起使用,会削弱它们值的含义。
    3.1.2 浮点类型
    诸如3.14159和6.023*10^23这样的数值无法按整数存储。第一个数并非整数,而第二个数远远超过了计算机所能表达的范围。但是,它们可以用浮点数的形式存储。它们通常以一个小数以及一个以某个假定数为基数的指数组成。例如:
        .3243F*16^1   .1100100100010*2^2
    它们所表示的值都是3.14159。用于表示浮点值的方法有很多,标准并未规定必须使用某种特定的格式。
    浮点数家族包括float、double和long double类型。通常,这些类型分别提供单精度、双精度以及在某些支持扩展精度的机器上提供扩展精度。ANSI标准规定long double至少和double一样长,而double至少和float一样长。标准同时规定了一个最小范围:所有浮点类型至少能够容纳10^-37~10^37之间的任何值。
    头文件float.h定义了名字FLT_MAX、DBL_MAX和LDBL_MAX,分别表示float、double和long double所能存储的最大值。而FLT_MIN、DBL_MIN和LDBL_MIN则分别表示float、double和long double能够存储的最小值。这个文件另外还定义了一些和浮点值实现有关的某些特性的名字,例如浮点数所使用的的基数、不同长度的浮点数的有效数字的位数等。
    浮点数字面值总是写成十进制的形式,它必须有一个小数点或一个指数,也可以两者都有。例如:
        3.14159 1E10 25. .5 6.023e23
    浮点数字面值在缺省情况下都是double类型的,除非它的后面跟一个L或l来表示它是一个long double类型的值,或者跟一个f或F来表示它是一个float类型的值。
    3.1.3 指针
    指针是C语言为什么如此流行的一个重要原因。指针可以有效地实现诸如tree和list这类高级数据结构。可以允许在指针上执行算术或比较操作,可以创建指向已经存在的数据对象的指针。所以,用C语言可以比使用其他语言编写出更为紧凑和有效的程序。
    变量的值存储于计算机的内存中,每个变量都占据一个特定的位置。每个内存位置都由地址唯一确定并引用,就像一条街道上的房子由它们的门牌号码来标识一样。指针只是地址的另一个名字罢了。指针变量就是一个其值为另外一个(一些)内存地址的变量。C语言拥有一些操作符,可以用来获得一个变量的地址,也可以通过一个指针变量取得它所指向的值或数据结构。
    指针也完全一样,你可以把计算机的内存想象成一条长街上的一间间房子,每间房子都用一个唯一的号码进行标识。每个位置包含一个值,这和它的地址是独立且显著不同的,即使它们都是数字。
    1.指针常量(pointer constant)
    指针常量和非指针常量在本质上是不同的,因为编译器负责把变量赋值给计算机内存中的位置。程序员实现无法知道某个特定的变量将存储到内存中的哪个位置。因此,你通过操作符获得一个变量的地址而不是直接把它的地址写成字面值常量的形式。事实上,当一个函数每次被调用时,它的自动变量(局部变量)可能每次分配的内存位置都不相同。因此,把指针常量表达为数值字面值的形式几乎没有用处,因此C语言内部并没有定义这个概念。
    NULL指针是一个例外,它可以用零值来表示。

    2.字符串常量(string literal)
    C语言不存在字符串类型,不过C语言提供了字符串常量。事实上,C语言存在字符串的概念:它就是一串以NUL字节结尾的零个或多个字符。这也是C语言没有显式的字符串类型的原因。由于NUL字节是用于终结字符串的,所以在字符串内部不能有NUL字节。之所以选择NUL作为字符串的终止符,是因为它不是一个可打印的字符。
    字符串常量的书写方式使用一对双引号包围一串字符,如下所示:
        "Hello" "\aWarning!\a" "Line 1\nLine2" ""
    最后一个例子说明字符串常量(不像字符常量)可以是空的。尽管如此,即使是空字符串,依然存在作为终止符的NUL字节。
    K&R C:
    在字符串常量的存储形式中,所有的字符和NUL终止符都存储于内存的某个位置。K&R C并没有提及一个字符串常量中的字符是否可以被程序修改,但它清楚地表明具有相同的值的不同字符串常量是分开存储的。因此,许多编译器都允许程序修改字符串常量。
    ANSI C则声明,如果对一个字符串常量进行修改,其效果是未定义的。它也允许编译器把一个字符串常量存储于一个地方,即使它在程序中多次出现。这就使得修改字符串常量变得极为危险,因为对一个常量进行修改可能殃及程序中其他字符串常量。因此,许多ANSI编译器不允许修改字符串常量,或者提供编译时选项,让你自行选择是否允许修改字符串常量。在实践中,请尽量避免这样做。如果需要修改字符串,请把它存储于数组中。
    程序中使用字符串常量会生成一个“指向字符的常量指针”。当一个字符串常量出现于一个表达式中时,表达式所使用的的值就是这些字符所存储的地址,而不是这些字符本身。因此,可以把字符串常量赋值给一个“指向字符的指针”,后者指向这些字符所存储的地址。但是,不能把字符串常量赋值给一个字符数组,因为字符串常量的直接值是一个指针,而不是这些字符本身。
    C函数库包含了一组函数,它们就用于操纵字符串,包括对字符串进行复制、连接、比较、计算字符串长度和在字符串中查找特定字符的函数。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值