【C++程序设计教程(第三版)钱能】 学习笔记 上半部/C++过程化语言基础

 

◆ 第一部分 C++过程化语言基础

 

>> 在生成可执行程序之前,C++忽略注释,并把每个注释都视为一个空格。

 

 

◆ 第2章 基本数据类型与输入/输出

 

>> 匈牙利标记法(Hungarian notation)

 

>> C++中,十进制数有正负之分,但八进制数和十六进制数只能表示无符号整数。

 

>> 如果在整型数后面加一个字母L或l,则认为是long int型整数。例如123L是long int型整数。

 

>> 123e5或123E5都表示123×105。要注意E或e的前面必须有数字,且E后面的指数必须为整数。

 

>> float型提供7位有效数字,double型提供15位有效数字,long double型提供19位有效数字。

在C++中,一个实型数如果没有任何说明,表示double型;要表示float型数,则必须在实数后加上f或F;要表示long double型数,则必须在实数后加l或L。

 

>> 反斜杠可以和八进制数或十六进制数值结合起来使用,以表示相应于该数值的ASCII码值。例如,'\03'表示Ctrl-C,'\0A'表示回车。转义字符使用八进制数表示时,最多是3位数,不必以0开头,如'\03'等价于'\3'。转义字符的八进制数表示的范围是'\000'~'\377',即从0~25510。转义字符使用十六进制数表示时,是2位数,用x或X引导,表示的范围是'\x00'~'\xff'。

 

>> '0'与0是截然不同的两个数。'0'是数字字符,其ASCII码等于值48或0x30。而0则是整数值。除此之外,'\0'和NULL也表示整数0。

 

>> 如果没有专门指定,第1个符号常量的枚举值就是0,其他枚举值依次为1往上加。

 

>> 在VC中会得到一个“不能将整常数赋给枚举变量”(cannot convert from'const int'to'enum paint')的错误。在BC中会得到一个“将整数赋给枚举变量paint”的警告。

 

>> 定义成const后的常量,程序中对其只能读不能修改,从而可以防止该值被无意地修改。由于不可修改,不能赋值,所以,常量定义时必须初始化。

 

>> 在C++编程中,常量定义都用const,不用#define。

 

>> 使用setprecision(n)可控制输出流显示浮点数的数字个数。C++默认的流输出数值有效位数是6。

 

>> 如果setprecision(n)与fixed合用,可以控制小数点右边的数字个数。fixed是设置定点小数表示法。

如果与scientific合用,可以控制指数表示法的小数位数。scientific是设置指数方式的小数表示法。

 

>> 在普通表示的输出中,setprecision(n)表示有效位数。

 

>> 在确定表示的输出中,setprecision(n)表示小数位数。

 

>> 在指数形式输出时,setprecision(n)表示小数位数。

 

>> 其有效位数沿用上次的设置值

 

>> 小数位数截短显示时,进行四舍五入处理。

 

>> 不同于其他控制符,setw(n)仅仅影响下一个数值输出,换句话说,使用setw设置的间隔方式并不保留其效力。

 

>> setw可以用来确定显示的宽度。默认时,流使用空格符来保证字符间的正确间隔。用setfill控制符可以确定setw所规定的间隔字符。setfill在头文件iomanip中定义。

 

>> 默认时,I/O流左对齐字串,右对齐数值。使用left和right标志,可以控制输出对齐。

 

>> 当需要显示小数点时,可以用showpoint标志。

 

>> 默认时,I/O流仅在负数之前显示值的符号,根据程序的用途,有时也需要在正数之前加上正号,可以用showpos标志。

 

>> 如果一个长整型数只按整型数输出,则会引起整数截断,即输出长整型数低位2个字节的值。上例中最后一个数的输出就是截断长整型数289868得到的输出。时下的绝大多数编译器都是32位的,即整型数在内部用32位表示,长整型就是整型,%ld与%d效果一样,因而最后的输出不会进行截断。

 

>> %o和%x用来以八进制数和十六进制数输出。八进制数和十六进制数都是无符号整数,输出时不带符号。

 

>> 当用%X时,输出十六进制数时用大写字母;当用%x时,输出用小写字母。

 

>> “%-5.3s”中的负号表示左对齐,如果没有负号,则默认为右对齐。5表示格式宽度,3表示截取字符串中3个字符。

 

>> “%-7.2f”表示左对齐,总长度7位,小数位数2位。

 

>> %e用来以指数方式输出浮点数。指数的输出格式要求小数点前必须有且只有一位非0数字,默认的小数位数为6,一般float和double默认的指数位数为2(不包括e+或e-),long double默认的指数位数为3。

 

>> %g格式符,用以输出浮点数,它根据数值的大小,自动选取f格式或e格式(较短的一种)。

 

>> 如果要输出%本身,则双写%。

 

>> %d用以输入整数,可以带l表示长整数,带h表示短整数。

%c用以输入字符。

%o、%x用以输入八进制数和十六进制数。%lo和%lx分别表示长八进制数和长十六进制数。

%f用以输入浮点数,%lf和%Lf分别表示输入double型数和long double型数。

%e与%f作用相同。

%s用以输入字符串,以非空字符开始,以空字符或回车结束。

 

>> 在用“%c”格式输入时,空格字符和转义字符都作为有效字符输入。

 

 

◆ 第3章 表达式和语句

 

>> 左值(left value,缩写为lvalue)是能出现在赋值表达式左边的表达式。左值表达式具有存放数据的空间,并且存放是允许的。

 

>> 右值(right value,缩写为rvalue)只能出现在赋值表达式的右边。左值表达式也可以作为右值表达式。

 

>> 表中的操作符如重复出现,则第1次出现的是单目运算符,第2次出现的是双目运算符。

 

>> 

 

>> 空语句是只有一个分号而没有表达式的语句

 

>> 赋值构成一个表达式,因而它具有值。赋值表达式的值为赋值符左边表达式的值

 

>>

 

>> 转换总是朝表达数据能力更强的方向,并且转换总是逐个运算符进行的。

 

>> 对于增量和减量操作符,它要求操作数是左值,因为操作数的值要发生变化。

 

>> 由于前增量操作返回的值即修改后的变量值,所以返回的仍是一个左值。

 

>> 由于后增量操作返回的值是原先a的数值,而后a的实体值已经发生变化,故返回的不能是当前的a值,只能是过去的a数值,不能是左值。

 

>> “(x?x:a)=2”中,尽管x是左值,a也是左值,但x与a不同类型,条件运算符要对其进行操作数的隐式转换,使之成为相同的类型。任何被转换的变量都不是左值。

 

>> 在C中,条件运算符是不能作左值的,所以“(x?a:b)=1;”将通不过编译。

 

>> 逗号表达式的值为第n个子表达式的值,即表达式n的值。

 

>> C++中,如果逗号表达式的最后一个表达式为左值,则该逗号表达式为左值。

 

>> 在C中,逗号表达式是不能作左值的,所以“(a=1,b,c+1,d)=5;”将通不过编译。

 

>> 在表达式中,各操作数的求值次序并没有在ANSI C++标准中规定。于是各个编译器为提高产生目标代码的质量,在不破坏操作符的优先级和结合性的前提下,对操作数(是个表达式)访问进行必要的顺序安排。

 

>> 表达式和语句的一个重要差别是:表达式具有值,而语句是没有值的。

 

>> 副作用是一个表达式中的嵌套表达式,在提供值的同时,又对某处变量进行修改所引起的。对于副作用,由于其运算结果的不可预料性,所以要尽量避免。

 

 

◆ 第4章 过程化语句

 

>> switch后面括号中的表达式只能是整型、字符型或枚举型。case后面的常量表达式类型必须与其匹配。

 

>> 因为case语句起语句标号的作用,所以case与default并不改变控制流程。

 

>> case通常与break语句联用,以保证多路分支的正确实现。

 

>> 多个case可以共用一组执行语句。例如:

 

>> switch语句可以嵌套。case与default标号是与包含它的最小的switch相联系的。

 

>> goto语句将控制从它所在的地方转移到语句标号处。

 

 

◆ 第5章 函数

 

>> C++不允许函数定义嵌套,即在函数定义中再定义一个函数是非法的。

 

>> 函数原型不必包含参数的名字,而只要包含参数的类型。

 

>> 一个程序将操作系统分配给其运行的内存块分为4个区域:

(1)代码区,存放程序的代码,即程序中的各个函数代码块。

(2)全局数据区,存放程序的全局数据和静态数据。

(3)堆区,存放程序的动态数据。

(4)栈区,存放程序的局部数据,即各个函数中的数据。

 

>> 可以在程序中间的任何地方定义全局变量,但要在任何函数之外。全局变量定义之前的所有函数定义,不会知道该变量。

 

>> 另外,局部变量的类型修饰是auto,表示该变量在栈中分配空间,但习惯上都省略auto。

 

>> 静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,每次调用该函数时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束。静态局部变量的初始化与全局变量类似,如果不为其显式初始化,则C++自动为其初始化为0。

 

>> 静态局部变量与全局变量共享全局数据区,但静态局部变量只在定义它的函数中可见。静态局部变量与局部变量在存储位置上不同,使得其存在的时限也不同,导致对二者操作的运行结果也不同。

 

>> 编译器看到inline后,为该函数创建一段代码,以便在后面每次碰到该函数的调用都用相应的一段代码来替换。内联函数可以在一开始仅声明一次。

 

>> 内联函数必须在被调用之前声明或定义。

 

>> 内联函数中不能含有复杂的结构控制语句,如switch和while。如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码。

另外,递归函数(自己调用自己的函数)是不能被用来做内联函数的。

内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销占比相对来说微不足道,所以也没有必要用内联函数实现。

 

>> C++可以给函数定义默认参数值。

 

>> 默认参数在函数声明中提供,当又有声明又有定义时,定义中不允许默认参数。如果函数只有定义,则默认参数才可出现在函数定义中。

 

>> 如果一个函数中有多个默认参数,则形参分布中,默认参数应从右至左逐渐定义。当调用函数时,只能向左匹配参数。

 

>> C++必须知道函数的返回类型以及接受的参数个数和类型,如果函数的定义出现在函数调用之后,就必须在程序的开始部分用函数原型进行说明。

 

>> 静态局部变量是在函数内部定义,但生命期却随函数的第一次被调用而产生,随程序的结束而结束,静态局部变量只能在定义该变量的函数中可见。

 

>> 内联函数是为了提高编程效率而实现的,它克服了用#define宏定义所带来的弊病。

 

>> 函数重载允许用同一个函数名定义多个函数。连接程序会根据传递给函数的参数数目、类型和顺序调用相应的函数。函数重载使程序设计简单化,程序员只要记住一个函数名,就可以完成一系列相关的任务。

 

 

◆ 第6章 程序结构

 

>> 所有函数声明一般都放在源文件的开始位置。

 

>> 在文件开头声明了带extern的int n,它表示该变量n不在本文件中分配空间,而在程序的其他文件中分配空间(变量定义)。

 

>> 带extern的变量说明是变量声明,不是变量定义。

 

>> 在全局变量前加一个static,使该变量只在这个源文件中可用,称之为全局静态变量。全局静态变量就是静态全局变量。

 

>> 在多文件组成的程序里,全局变量与全局静态变量是不同的。全局静态变量使得该变量成为由定义该变量的源文件所独享。

 

>> 在文件作用域下声明的inline函数默认为static存储类型。在文件作用域下声明的const的常量也默认为static存储类型。它们如果加上extern,则为外部存储类型。

 

>> 作用域是标识符在程序中有效的范围,标识符的引入与声明有关,作用域开始于标识符的声明处。C++的作用域范围分为局部作用域(块作用域)、函数作用域、函数原型作用域、文件作用域和类作用域(见11.7节)。

 

>> →在C++早些时候的版本,例如TC++3.0,允许for语句头中声明的变量的作用域延伸至包含for的最小块结束。后来的ANSI C++标准规定,for语句头中声明的变量的作用域只在该for语句中。但有些编译器仍保留其另一种意义的选择。如BORLAND C++5.0在Options|Project|C++Options|C++Compatibility中,可以选择for语句头中声明的变量的作用域范围。

 

>> 标号是唯一具有函数作用域的标识符。goto语句使用标号。标号声明使得该标识符在一个函数内的任何位置均可以被使用。

 

>> 习惯上,在函数原型声明中,都为参数指定一个有说明意义的标识符,而且一般总是与该函数定义中参数的标识符一致。

 

>> 可见性与作用域是一致的。作用域指的是标识符有效的范围,而可见性是分析在某一位置标识符的有效性。

 

>> 如果被隐藏的是全局变量,则可用符号::来引用该全局变量。

 

>> 生命期也叫生存期。生命期与存储区域密切相关,存储区域主要有代码区(code area)、数据区(data area)、栈区(stack area)和堆区(heap area),见图5-2,对应的生命期为静态生命期、局部生命期和动态生命期。

 

>> 变量在固定的数据区中分配空间的,具有静态生命期。所以,全局变量、静态全局变量、静态局部变量都具有静态生命期。具有文件作用域的变量具有静态生命期。

 

>> 静态生命期的变量,若无显式初始化,则自动初始化为0。

 

>> 函数驻在代码区,也具有静态生命期。在函数内部可以声明静态生命期的变量,即静态局部变量。

 

>> 在函数内部声明的变量或者是在块中声明的变量具有局部生命期。这种变量的生命期开始于程序执行经过其声明点时,而结束于其作用域结束处。所以具有局部生命期的变量也具有局部作用域。但反之不然,具有局部作用域的变量若为局部变量,则具有局部生命期;若为静态局部变量,则具有静态生命期。静态局部变量的生命期是从定义它的函数第一次被调用时开始存在,直到程序运行结束。

 

>> 具有局部生命期的变量驻在内存的栈区。

 

>> 具有局部生命期的变量如果未被初始化,则内容不可知。

 

>> 动态生命期

这种生命期由程序中特定的函数调用(malloc()和free())或操作符(new和delete)来创建和释放,见8.4节。

具有这种生命期的变量驻在内存的堆中。当用函数malloc()或new为变量分配空间时,生命期开始;当用free()或delete释放该变量的空间或程序结束时,生命期结束。

 

>> 源文件中含有包含头文件的预编译语句,经过预编译后,产生翻译单元,该翻译单元以临时文件的形式存放在计算机中。之后编译,进行语法检查,产生目标文件(.obj)。若干个目标文件经过连接,产生可执行文件(.exe)。连接包括C++库函数的连接和标准类库的连接。

 

>> 预处理程序对源文件进行第一次处理,它处理的是预处理指令。我们介绍三类预处理指令:#include、#define和#if。

 

>> include文件可以嵌套,即在头文件中还可以有包含指令。

 

>> 条件编译的指令有#if、#else、#elif、#endif、#ifdef、#ifndef和#undef。

 

 

◆ 第7章 数组

 

>> 编程时,如果要定义一个很大的数组,可以通过将其定义为静态或全局来解决,也可以将其在堆内存中分配(见8.4节)。

 

>> 初始化数组的值的个数不能多于数组元素个数,初始化数组的值也不能通过跳过逗号的方式来省略,这在C中是允许的,但在C++中不允许。

 

>> 初始化值的个数可少于数组元素个数。当初始化值的个数少于数组元素个数时,前面的按序初始化相应值,后面的初始化为0。

 

>> 由于传递数组实际上传递的是地址,所以函数原型中,数组参数的书写形式无须在方括号中写明数组大小。如果写明了数组大小,编译器将忽略之。数组形参的空方括号只是告诉函数,该参数是个数组的起始地址。

 

>> 如果对全部元素赋初值,则定义数组时对第一维的大小可以忽略,但第二维的大小不能省。

 

>> 作为参数传递一个二维数组给函数,其意义也为内存地址,所以原型中,声明整数数组参数的形式只能省略左边的方括号。

 

>> 由于二维数组在内存中是线性排列的,传递一维数组和传递二维数组都是传的地址,所以可以在被调用的函数中用单重循环来遍历二维数组中的所有元素,此时只需传递数组名和元素总个数。要注意被传递的数组地址不要用数组名表示,要用第一个元素的地址表示,因为数组名表示的是二维数组的首地址,尽管地址值相同,但操作不同,在第8章中将详细解释地址与指针的差别。

 

 

◆ 第8章 指针

 

>> 指针是一个内存实体,具有值。要使用指针,就必须定义指针。指针有指针常量和指针变量之分,定义指针通常定义的是指针变量,即可以随时改变指针的指向。所以,指针与指针变量经常划等号。

 

>> *放在可执行语句中的指针之前,为间接引用操作符;*放在指针定义中时,为指针定义符。

 

>> 由于指针是具有某个数据类型的地址,所以指针运算都是以数据类型为单位展开的。

 

>> 数组名可以拿来初始化指针,数组名就是数组第一个元素地址。即对于数组a,有a等于&a[0]。

 

>> a[i]表示数组的第i个元素的值,而a+i表示第i个元素的地址,对其间接访问,即*(a+i)就表示第i个元素的值。另外,下标操作是针对地址而不仅仅是针对数组名的,所以iPtr[i]也表示第i个元素的值。

 

>> 数组名是指针常量,区别于指针变量,所以,给数组名赋值是错误的。

 

>> 堆(heap)是内存空间。堆是区别于栈区、全局数据区和代码区的另一个内存区域。堆允许程序在运行时(而不是在编译时)申请某个大小的内存空间。

 

>> 因为malloc()函数并不知道用这些内存干什么,所以它返回一个没有类型的指针(见8.6节)。但对整数指针ap来说,malloc()函数的返回值必须显式转换成整数类型指针才能被接受(ANSI C++标准)。

 

>> new和delete是C++专有的操作符,它们不用头文件声明。

 

>> 在指针定义语句的类型前加const,表示指向的对象是常量。

 

>> 定义指向常量的指针只限定指针的间接访问只能读而不能写,而没有限定指针值的读写访问性。

 

>> 常量指针定义“const int*pi=&a;”告诉编译,*pi是常量,不能将*pi作为左值进行操作。

 

>> 在指针定义语句的指针名前加const,表示指针本身是常量。

 

>> pc是指针常量,在定义指针常量时必须初始化,就像常量初始化一样。

 

>> 指针常量定义“int*const pc=&b;”告诉编译,pc是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pc可以修改。

 

>> 常量指针常量定义“const int*const cpc=&b;”告诉编译,cpc和*cpc都是常量,它们都不能作为左值进行操作。

 

>> 传递的数组参数在Sum()中,实质上是一个指针,所以声明Sum(int array[],int n)与Sum(int*array,int n)是等价的。

 

>> 可以返回堆地址,可以返回全局或静态变量的地址,但不要返回局部变量的地址。

 

>> 程序中两个字符串的比较实质上是两个地址的比较。在编译时,给了这两个字符串不同的存放地点,所以两个“join”字符串的地址是不同的。要使两个字符串真正从字面上进行比较,可以用库函数strcmp()

 

>> 输出字符指针就是输出字符串。所以输出pc时,便从'e'字符的地址开始,直到遇到'\0'结束。

输出字符指针的间接引用,就是输出单个字符。当输出*pc时,便是输出pc所指向的字符

 

>> C++中可以用字符串去初始化字符数组,但是不能对字符数组赋予一个字符串,原因是数组名是常量指针,不是左值。

 

>> NULL与void*是不同的概念,NULL是一个值,一个指针值,任何类型的指针都可赋予该值;而void*是一种类型,它定义无类型指针。

 

>> C++程序只不过是操作系统调用的函数。

 

>> 操作系统启动程序时,总是把3个参数(int argc,char*argv[],char*env[])传递给main()函数,env用得很少,这里不作介绍。程序可以按下面4种方式来声明main()函数:

• int main()

• int main(int argc)

• int main(int argc,char*argv[])

• int main(int argc,char*argv[],char*env[])

其中第二种情况是合法的,但不常见,因为在程序中很少有只用argc,而不用argv[]的情况。

在命令行参数中,有时某个参数含有空格,而操作系统是以空格作为区分下一个参数的标志。解决的方法是将该参数用引号括起来。

 

>> 在程序运行中,全局变量存放在data区,局部变量存放在栈区,申请的动态空间存放在堆区。

 

>> 函数代码是程序的算法指令部分,它们同样也占有内存空间,存放在代码(code)区。每个函数都有地址。指向函数地址的指针称为函数指针。函数指针指向代码区中的某个函数,通过函数指针可以调用相应的函数。

 

>> 不含下标访问(即方括号[])的数组名是地址,不作函数调用(即括号())的函数名也是地址,所以可以将省略了()的函数名作为函数地址赋给函数指针。

 

>> 指针不仅仅是地址,还有对数据类型的操作性规定。这是理解指针的关键。

 

 

◆ 第9章 引用

 

>> 引用运算符只在声明的时候使用,它放在类型名后面

 

>> 引用虽在语法上代表一种类型,但在概念上只是其他实体的附体,所以对同一实体可以定义多个引用,但对不存在的引用实体,就没有引用的引用,也没有指向引用实体的指针(所谓引用的指针)。

 

>> 有空指针,无空引用。

 

>> 由于返回的是引用,所以可以作为左值直接进行增量操作。

 

>> C++不区分变量的const引用和const变量的引用。程序绝不能给引用本身重新赋值,使它指向另一个变量,因此引用总是const的。如果对引用应用关键词const,其作用就是使目标成为const变量。

 

 

◆ 第10章 结构

 

>> 声明一个结构并不分配内存,内存分配发生在定义这个新数据类型的变量中。

 

>> 数组的[]运算符与结构的点运算符具有相同的运算优先级,它们是所有运算符中优先级最高的。

 

>> 在C中(而不是C++),结构变量定义在结构类型名前必须有struct关键字。

 

>> 当用点操作符时,它的左边应是一个结构变量;当用箭头操作符时,它的左边应是一个结构指针。

 

>> 当结构很大时,引用传递的优越性才真正开始体现。

 

>> 结构成员不能是自身的结构变量,但可以用自身结构指针作为成员。

 

>> 在堆中分配结构空间时,我们看到new操作符比malloc()函数的使用更简便。

 

  • 11
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值