K&R笔记(二)

Points

第二章介绍数据类型、运算符与表达式的知识,第三章则介绍了C中的控制流,第四章以逆波兰计算器展开介绍函数和程序结构。

  • 没有后缀的浮点数常量为double类型,表达式中float类型的操作数不会自动转换成double类型

  • 常量表达式在编译时求值

  • 任何变量的声明都可以使用const限定符限定,该限定符指定变量的值不能修改

  • 为了保证程序的可移植性,如果要在char类型的变量中存储非字符数据,最好指定signedunsigned限定符

  • signed类型进行按位运算右移操作时,有些机器是算术移位(用符号位填补左边空出的部分),有些是逻辑移位(用0填补左边空出的部分)。算术右移可以进行sign类型的除法运算,右移n位就等于除2的n次方。

  • 表达式x &= (x-1)可以删除x中最右边值为1的一个二进制位

  • 函数调用、嵌套赋值语句、自增与自减运算符都有可能产生副作用(side effect),在对表达式求值的同时,修改了某些变量的值。

  • 在任何一种编程语言中,如果代码的执行结果与求值顺序相关,则都是不好的程序设计风格

  • ;是语句结束符;,是C语言优先级最低的运算符,适用于关系紧密的结构中,应该慎用

  • C语言中,任何标识符(除了goto的label以及main()main)在使用之前都一定要声明

  • 使用宏可以节省大量代码,是一种元语言

  • #undef可以取消标识符预处理器的定义

作用域 · 链接 · 存储类

标识符(identifier)可以指代多种实体,如对象、函数、结构标记等。其中的对象也称为变量,指的是一个存储位置,对其的解释依赖两个属性:存储类(storage class)和类型(type)。存储类指存储变量值的内存类型,决定其标识对象的存储区域的生存期(lifetime);类型决定标识对象中值的含义。此外,作为标识符还具有一个作用域(scope)和一个连接(linkage),作用域指程序中可以访问此名字的区域,连接决定另一作用域中的同一个名字是否指向同一个对象或函数。标识符的作用域与它的链接属性有关,但两个概念并不相同。
编译器可以确认四种作用域,文件作用域(file scope)、函数作用域(function scope)、代码块作用域(block scope)和原型作用域(prototype scope)。块结构中声明的标识符具有代码块作用域。嵌套的块结构中,内层的标识符可以屏蔽外层的标识符,因此标识符有一样的名字也是可以的,不过这不是好的设计风格。在非嵌套的状态,任何时刻都只至多允许一个块结构处于活动状态,不同代码块的变量可以共享同一个内存地址。任何在所有代码块之外声明的标识符具有文件作用域,表示这些标识符从声明之处直到所在源文件结尾处都是可以访问的。值得注意的是,通过#include指令包含到其他文件的声明,就好像在原文件中声明一样,作用域并不局限于头文件结尾,而延续到当前文件。原型作用域只适用于函数原型中声明的参数名,防止参数名与程序其他部分的名字冲突。函数作用域只用于goto语句,《C和指针》劝我们不要碰它。
链接属性包含三种情况,外部(external)、内部(internal)和无(null)。没有链接属性的标识符总是被当作单独个体,有多少个声明就有多少个独立实体。具有内部链接属性的标识符在同一个源文件中总是指向同一个实体,具有外部链接属性的标识符不论声明多少次、位于几个源文件都表示同一个实体。关键字externstatic用于在声明中修改标识符的链接属性。C语言中,对于函数而言,因为不允许嵌套定义,它总是外部的,缺省状态下具有外部链接;对于变量,只要其并非声明于代码块或函数定义内部,缺省状态下亦 具有外部链接属性。如果某个声明具有缺省external属性,使用static可以使它的链接属性变成internal;extern为一个标识符指定external链接属性,并且只对第一个声明有效。另外,有时我们不必显示使用extern关键字,就如上所指明的缺省情况,但是显式使用更能表达我们的意图,增加代码的可读性。
变量的存储类有两类,自动存储类( automatic)和静态存储类(static),存储类由声明变量时使用的一些关键字和上下文共同决定。程序运行时,计算机有三个地方可以用于存储变量,普通内存、运行时堆栈、硬件寄存器。静态存储类的变量(静态变量)存储在普通内存中,自动存储类的变量(自动变量)存储在硬件寄存器或运行中的堆栈中。静态变量在程序运行之前创建,在执行期间一直存在,除非给其赋值,不然存储的值将保持不变。此外,我们无法为静态变量指定其他的存储类型。自动变量在执行时创建,代码块执行完后便自行销毁。声明中所涉及的标识符有autoregisterstatic等三个,前两个用来声明自动存储类对象,auto使用频率低,基本可以忽略;register声明的对象表明该对象使用频率高,希望放在寄存器中,但编译器可以忽略此选项,不予理睬;最后的关键字static可以用在代码块内部声明的变量,将其存储类从自动存储类变为静态存储类。缺省状态时,存储类由上下文决定,凡是在所有代码块之外声明的变量都是静态变量,存储在普通内存;凡是在块结构中声明的变量都是自动变量,存储在运行时的堆栈中。
具有外部链接属性的变量称为全局变量,与之对应的是局部变量,它是函数的私有变量,不可在函数外部通过名字访问。局部变量都是自动变量。

值得注意的是,声明与定义不同,并不引起存储器的分配,只是说明变量的属性。

字面值 · 常量

字面值(literal)类似我们平常所说的值,比如3.14、0等。我们可以用#define为字面值起符号别名,此后别名可以出现在任何使用该值的地方。常量与变量一样,只是值不允许改变,用关键字const声明。C中的字符串常量(string literal)就是字符数组,程序在使用字符串常量的时候会生成一个指向字符的常量指针,是其存储地址而不是字符本身,所以我们不可以将字符串常量赋给字符数组。


参考: C和指针