(整理说明:本资料是我在网上无意间找到的,读起来感觉不错,但由于原文是每章一个网页的格式,读起来不是很习惯,而且也不方便保存,所以我花了2个多小时的时间将所有网页的内容综合整理了一下,但最后才发现,文章的顺序颠倒了,所以各位如果愿意阅读本文的话,请从后面向前读,每个红色“标题”代表一章,如有不便还请各位见谅,或到原文网站阅览)
标题:
:
类
型
转换
之
隐
式
转换
对
于我
们
来
说
,
3+1.5=4.5
。但是
对
于
计
算机来
说
,
这
两个数的相加可不
这么简单
。因
为
3
与
3.0
是不同的数据
类
型,
3.0
与
1.5
是可以相加的,
3
却不能与
1.5
相加。于是,
C++
在
对
上面的表达式
进
行
处
理
时
,有必要
对
其中一个(或两者)
进
行
转换
。
因 为这 个 转换 是 “ 隐 式 ” 的,也就是 说这 个 转换 不 让 程序 员 知道,那 么 ,系 统 就不能必 须 保 证 不 产 生 损 失, 这 个 损 失指的是精度。 为 了不 损 失精度,数据 总 是向精度高的 类 型 转换 。惟一的例外是当某个 变 量用作条件 时 ,它被 转换为 bool 型。
对 算 术类 型的 转换 是 这样 的:所有比 int 小的都 转为 int 或 unsigned int ,即使没有必要也 这么转 。原因很 简单 ,因 为 int 的 长 度正好等于字 长 。 对 CPU 来 说 ,一次 处 理一个字是最快的。如果 int 或 unsigned int 无法达到要求, 则 往 long 、 double 转 化。
如果 每 一个 转换 都 能不造成 损 失,那自然是好事。可是世 间 的事 总 有不随人愿的 时 候。 对 于同一 种 算 术类 型,其 signed 和 unsigned 所能表达的范 围 是一 样 大,但却是互不重叠的两个范 围 。就像 “ 妇联 ” 和 “ 工会 ” 一 样 ,往哪 边转换 都可能会 产 生 损 失。 这 是无法解决的 问题 ,所以,在 VC 中, 试图 比 较 int 和 unsigned int 变 量 时 会 显 示警告。
奇怪的是,据我 测试 ,在 VC++.net 中,只有 进 行 “<” 和 “>” 比 较 的 时 候才会 显 示警告,而 进 行 “==” 和 “!=” 比 较 的 时 候却不 显 示警告。 实际 上 这 两个比 较 也会有同 样 的 问题产 生,比如以下代 码 :
int a = -3;
unsigned b = 4294967293;
if (a == b) cout << "yes" << endl;
测试 运行以上代 码 会 发现 表达式 a==b 的 值为 true 。 这 是从 int 转为 unsigned int 过 程中的副作用, 这 个副作用我 们应该 知道,但是 VC++.net 不 进 行任何警告似乎也有些与理不通。 —— 难 道我 关闭 了某些警告?
其它 隐 式 转换还 包括数 组 名 转换为 指 针 、算 术值 用作条件 时转换为 bool ,枚 举 被 转换为 整数,非 const 对 象 转换为 const 对 象等。其中枚 举转换为 整数没什 么 要提的,枚 举值 本来就是整数的 别 名,非 const 对 象 转为 const 对 象只是 临时 声明它的保 护级别 ,通常用于作 为 参数 传递时 。
因 为这 个 转换 是 “ 隐 式 ” 的,也就是 说这 个 转换 不 让 程序 员 知道,那 么 ,系 统 就不能必 须 保 证 不 产 生 损 失, 这 个 损 失指的是精度。 为 了不 损 失精度,数据 总 是向精度高的 类 型 转换 。惟一的例外是当某个 变 量用作条件 时 ,它被 转换为 bool 型。
对 算 术类 型的 转换 是 这样 的:所有比 int 小的都 转为 int 或 unsigned int ,即使没有必要也 这么转 。原因很 简单 ,因 为 int 的 长 度正好等于字 长 。 对 CPU 来 说 ,一次 处 理一个字是最快的。如果 int 或 unsigned int 无法达到要求, 则 往 long 、 double 转 化。
如果 每 一个 转换 都 能不造成 损 失,那自然是好事。可是世 间 的事 总 有不随人愿的 时 候。 对 于同一 种 算 术类 型,其 signed 和 unsigned 所能表达的范 围 是一 样 大,但却是互不重叠的两个范 围 。就像 “ 妇联 ” 和 “ 工会 ” 一 样 ,往哪 边转换 都可能会 产 生 损 失。 这 是无法解决的 问题 ,所以,在 VC 中, 试图 比 较 int 和 unsigned int 变 量 时 会 显 示警告。
奇怪的是,据我 测试 ,在 VC++.net 中,只有 进 行 “<” 和 “>” 比 较 的 时 候才会 显 示警告,而 进 行 “==” 和 “!=” 比 较 的 时 候却不 显 示警告。 实际 上 这 两个比 较 也会有同 样 的 问题产 生,比如以下代 码 :
int a = -3;
unsigned b = 4294967293;
if (a == b) cout << "yes" << endl;
测试 运行以上代 码 会 发现 表达式 a==b 的 值为 true 。 这 是从 int 转为 unsigned int 过 程中的副作用, 这 个副作用我 们应该 知道,但是 VC++.net 不 进 行任何警告似乎也有些与理不通。 —— 难 道我 关闭 了某些警告?
其它 隐 式 转换还 包括数 组 名 转换为 指 针 、算 术值 用作条件 时转换为 bool ,枚 举 被 转换为 整数,非 const 对 象 转换为 const 对 象等。其中枚 举转换为 整数没什 么 要提的,枚 举值 本来就是整数的 别 名,非 const 对 象 转为 const 对 象只是 临时 声明它的保 护级别 ,通常用于作 为 参数 传递时 。
标题:
:内存管理之
new
和
delete
林
锐
博士曾将内存管理比
喻为
“
雷区
”
(《高
质
量
C++/C
编
程指南》第
44
页
),内存管理
这块难
不
难
?恐怕不好
说
。
“
会者不
难难
者不会
”
嘛。但是
说
内存管理
这块难
以成
为
“
会者
”
,
应该
是没有
错
的。
程序 时时 刻刻与内存打交道,只不 过 以往我 们 不用考 虑 ,甚至不用知道。所以,所 谓 “ 内存管理 ” ,是特指堆内存。
如果把堆内存和 栈 内存的使用放在一起考 虑 ,可以降低 对 内存管理恐惧。
一、内存的分配:
int i(100);// 栈 上分配内存
int *pi = new int(100);// 堆上分配内存
以上两 种 分配,都使用了 100 作 为 初始 值进 行初始化,如果是 进 行 类对 象的分配,它 们还 可以指定使用哪个构造函数,比如:
CString s(s1);// 栈 上分配内存
CString *ps = new CString(s1)// 堆上分配内存
这 里的 s 可以是 char* 指 针 ,也可以是另一个 CString 对 象,它的 类 型将决定上面 这 两行 语 句 调 用哪一个构造函数。
在 这 里,有一点要特 别说 明,如果要使用默 认 构 造函数, 则 new 语 句后面可以用空括号 对 ,而 栈 内分配的 语 句切不可用空括号 对 。如果写成 “CString s();” , 则 并不是定 义 一个 CString 对 象 s ,而是定 义 一个返回 值为 CString 的函数 s 。
上面两 种 分配,也都可以分配 对 象数 组 ,不同的是,用 new 操作符在堆内存分配数 组时 ,只能 调 用默 认 构造函数。而在 栈 上分配却可以指定 对 象成 员 的初始 值 。如:
int a[3] = {1,2,3};// 栈 上分配内存, int 可以 换 成其它 类 型名,后面的初始 值 可作相 应调 整。
int *p = new int[3];// 不能指定 这 三个 对 象的初始 值
二、内存的 访问 :
栈 内存可以通 过对 象 访问 ,也可以通 过 指 针访问 ,堆内存通 过 指 针访问 。方法完全相同。
三、内存的 释 放:
栈 内存在 对 象作用域 结 束后自 动释 放,堆内存要用 delete 。
delete pi;// 释 放内存
delete []p;// 释 放 对 象数 组
对 于 释 放 对 象数 组 ,那个空的 [] 对 不可以 丢 ,否 则 将只 释 放数 组 的第一个元素。 导 致内存泄露。
有了以上 对 比,堆内存似乎没有了任何 难 度。那 么 内存管理的玄机究竟在哪儿呢?在 进 行内存分配与 释 放的 时 候,有几个注意点要 记 住:
1 、 new 操作有可能失 败 ,当系 统 无法分配需要的内存 块时 ,将返回 NULL 值 ,所以在 new 操作之后,要立即判断 pi 的 值 是否 为 NULL 。
int *pi = new int(100);
if (pi = NULL) {...}
2 、堆上分配的内存必 须 delete ,而且只可以 delete 一次。 为 了保 证 内存只被 delete 一次, 请务 必 记 住 delete 以后立即将指 针设为 NULL :
delete pi;
pi = NULL;
虽 然 “pi=NULL;” 不是必 须 的,但 这 是个好 习惯 。将指 针设为 NULL 既可以防止 继续读 写 该 内存,也可以防止再次 释 放 该 内存。
老的 C 程序 员 可能忘不了 malloc 和 free 函数,它 们 也可以 进 行内存的分配与 释 放。但是 C++ 时 代它 们 已 经 落伍了。它 们 只是按 请 求的字 节 数 进 行分配,而不管你用 这块 内存来干什 么 。 这样 做,就等于放弃了 类对 象的构造与析构。 对 于很多 类 来 说 , 这样 做是很危 险 的 。
程序 时时 刻刻与内存打交道,只不 过 以往我 们 不用考 虑 ,甚至不用知道。所以,所 谓 “ 内存管理 ” ,是特指堆内存。
如果把堆内存和 栈 内存的使用放在一起考 虑 ,可以降低 对 内存管理恐惧。
一、内存的分配:
int i(100);// 栈 上分配内存
int *pi = new int(100);// 堆上分配内存
以上两 种 分配,都使用了 100 作 为 初始 值进 行初始化,如果是 进 行 类对 象的分配,它 们还 可以指定使用哪个构造函数,比如:
CString s(s1);// 栈 上分配内存
CString *ps = new CString(s1)// 堆上分配内存
这 里的 s 可以是 char* 指 针 ,也可以是另一个 CString 对 象,它的 类 型将决定上面 这 两行 语 句 调 用哪一个构造函数。
在 这 里,有一点要特 别说 明,如果要使用默 认 构 造函数, 则 new 语 句后面可以用空括号 对 ,而 栈 内分配的 语 句切不可用空括号 对 。如果写成 “CString s();” , 则 并不是定 义 一个 CString 对 象 s ,而是定 义 一个返回 值为 CString 的函数 s 。
上面两 种 分配,也都可以分配 对 象数 组 ,不同的是,用 new 操作符在堆内存分配数 组时 ,只能 调 用默 认 构造函数。而在 栈 上分配却可以指定 对 象成 员 的初始 值 。如:
int a[3] = {1,2,3};// 栈 上分配内存, int 可以 换 成其它 类 型名,后面的初始 值 可作相 应调 整。
int *p = new int[3];// 不能指定 这 三个 对 象的初始 值
二、内存的 访问 :
栈 内存可以通 过对 象 访问 ,也可以通 过 指 针访问 ,堆内存通 过 指 针访问 。方法完全相同。
三、内存的 释 放:
栈 内存在 对 象作用域 结 束后自 动释 放,堆内存要用 delete 。
delete pi;// 释 放内存
delete []p;// 释 放 对 象数 组
对 于 释 放 对 象数 组 ,那个空的 [] 对 不可以 丢 ,否 则 将只 释 放数 组 的第一个元素。 导 致内存泄露。
有了以上 对 比,堆内存似乎没有了任何 难 度。那 么 内存管理的玄机究竟在哪儿呢?在 进 行内存分配与 释 放的 时 候,有几个注意点要 记 住:
1 、 new 操作有可能失 败 ,当系 统 无法分配需要的内存 块时 ,将返回 NULL 值 ,所以在 new 操作之后,要立即判断 pi 的 值 是否 为 NULL 。
int *pi = new int(100);
if (pi = NULL) {...}
2 、堆上分配的内存必 须 delete ,而且只可以 delete 一次。 为 了保 证 内存只被 delete 一次, 请务 必 记 住 delete 以后立即将指 针设为 NULL :
delete pi;
pi = NULL;
虽 然 “pi=NULL;” 不是必 须 的,但 这 是个好 习惯 。将指 针设为 NULL 既可以防止 继续读 写 该 内存,也可以防止再次 释 放 该 内存。
老的 C 程序 员 可能忘不了 malloc 和 free 函数,它 们 也可以 进 行内存的分配与 释 放。但是 C++ 时 代它 们 已 经 落伍了。它 们 只是按 请 求的字 节 数 进 行分配,而不管你用 这块 内存来干什 么 。 这样 做,就等于放弃了 类对 象的构造与析构。 对 于很多 类 来 说 , 这样 做是很危 险 的 。
标题:
:
优
先
级
、
结
合性和求
值顺
序
说
到
优
先
级
,我能熟
练
背出
“
先乘除,后加减
”
,之于
C++
列出的整整
19
个
优
先
级
,
每
个
优
先
级
又包含若干个操作符,我
总
是看了就
头
皮
发
麻。以我的
记
性,
连军
旗里哪个大哪个小都背不出来,
这
几十个操作符
——
还
是
饶
了我吧。
记 住林 锐 博士的 话 : “ 如果代 码 行中的运算符比 较 多,用括号确定表达式的操作 顺 序,避免使用默 认 的 优 先 级 。 ” (《高 质 量 C++/C 编 程指南》第 26 页 ) 这样 做最直接的作用是不用 记忆 了 复杂 的 优 先 级 了,不用 记忆 并不是因 为懒 ,而是 为 了更清晰。 毕 竟程序不只是 编给计 算机运行的,当我 们处 在一个多人 协 作的 团 体中 时 ,程序的清晰度和精确性比性能要高得多。再 说 ,多加几 对 括号是不影响运行效率的。
结 合性和求 值顺 序是容易混淆的两个概念。 每 一个操作符都 规 定了 结 合性,但是只有极少数操作符 规 定求 值顺 序。 结 合性是 说 如果有多个同 级别 的操作符, 这 些操作数 该 如何分 组 。比如 “1+2+3” 究竟分成 “(1+2)+3” 还 是 “1+(2+3)” , 虽 然 这 两 种 分 组 最 终 没有区 别 ,但不等于所有操作符都不 产 生区 别 。即使不 产 生区 别 , 计 算机 毕 竟是 计 算机,它只能按死的 规 范做事,于其 给 它灵 活机制, 还 不如 规 定了 结 合性 让 它遵守。
C++ 只有四个操作符 规 定了求 值顺 序,它 们 是 “&&” 、 “||” 、 “?:” 和 “,” , 记 住 这 四个操作符并不 难 。反 过 来 记 住其它操作符也不 难 , 难 的是在写程序中是否有 这 个意 识 。那 么 多网友 讨论 “j = i++ + i++ + i++;” 的 结 果,正 说 明了 还 有好多人不了解 “ 未定 义 ” 的威力。如果不小心使用了依 赖 于未定 义 求 值 程序的 语 句,将是一个不容易 发现 并改正的 问题 。 比如 “if (a[index++] < a[index]);”
记 住林 锐 博士的 话 : “ 如果代 码 行中的运算符比 较 多,用括号确定表达式的操作 顺 序,避免使用默 认 的 优 先 级 。 ” (《高 质 量 C++/C 编 程指南》第 26 页 ) 这样 做最直接的作用是不用 记忆 了 复杂 的 优 先 级 了,不用 记忆 并不是因 为懒 ,而是 为 了更清晰。 毕 竟程序不只是 编给计 算机运行的,当我 们处 在一个多人 协 作的 团 体中 时 ,程序的清晰度和精确性比性能要高得多。再 说 ,多加几 对 括号是不影响运行效率的。
结 合性和求 值顺 序是容易混淆的两个概念。 每 一个操作符都 规 定了 结 合性,但是只有极少数操作符 规 定求 值顺 序。 结 合性是 说 如果有多个同 级别 的操作符, 这 些操作数 该 如何分 组 。比如 “1+2+3” 究竟分成 “(1+2)+3” 还 是 “1+(2+3)” , 虽 然 这 两 种 分 组 最 终 没有区 别 ,但不等于所有操作符都不 产 生区 别 。即使不 产 生区 别 , 计 算机 毕 竟是 计 算机,它只能按死的 规 范做事,于其 给 它灵 活机制, 还 不如 规 定了 结 合性 让 它遵守。
C++ 只有四个操作符 规 定了求 值顺 序,它 们 是 “&&” 、 “||” 、 “?:” 和 “,” , 记 住 这 四个操作符并不 难 。反 过 来 记 住其它操作符也不 难 , 难 的是在写程序中是否有 这 个意 识 。那 么 多网友 讨论 “j = i++ + i++ + i++;” 的 结 果,正 说 明了 还 有好多人不了解 “ 未定 义 ” 的威力。如果不小心使用了依 赖 于未定 义 求 值 程序的 语 句,将是一个不容易 发现 并改正的 问题 。 比如 “if (a[index++] < a[index]);”
标题:
:
sizeof
和逗号操作符
把
sizeof
说
成操作符可能有些不合
习惯
,因
为
sizeof
的用法与函数没区
别
。但是
sizeof
与函数有着本
质
的区
别
:它是
编译时
常量。也就是
说
,在程序
编译时
,就会求出它的
值
,并且成
为
程序中的常量。
sizeof 本身比 较简单 ,惟一要提的就是它 对 数 组 名和指 针进 行操作的 结 果。
int a[10];
sizeof(a);
该 操作返回的是数 组 所有元素在内存中的 总长 度。但是如果 对 指 针进 行操作,返回的 则 是指 针 本身的 长 度,与指 针 所指 类 型无 关 。
正因 为 数 组 名与指 针 有着千 丝 万 缕 的 关 系,所以有 时 候 这 个特性会 让 人摸不着 头脑 :
int function(int a[10])
{
sizeof(a);
...
}
以上 sizeof 返回的不是数 组 所有成 员 的大小,而是指 针 的大小,因 为 数 组 在参数 传递 中弱化 为 指 针 。
逗号操作符除了在 for 语 句中 应 用以外,我没 发现 在哪儿 还 有用 处 。因 为 在一般情况下,逗号改成分号肯定是可以的,在 for 语 句中因 为 分号的作用另有定 义 ,所以不能随便改。 这 才有了逗号的用武之地 。
sizeof 本身比 较简单 ,惟一要提的就是它 对 数 组 名和指 针进 行操作的 结 果。
int a[10];
sizeof(a);
该 操作返回的是数 组 所有元素在内存中的 总长 度。但是如果 对 指 针进 行操作,返回的 则 是指 针 本身的 长 度,与指 针 所指 类 型无 关 。
正因 为 数 组 名与指 针 有着千 丝 万 缕 的 关 系,所以有 时 候 这 个特性会 让 人摸不着 头脑 :
int function(int a[10])
{
sizeof(a);
...
}
以上 sizeof 返回的不是数 组 所有成 员 的大小,而是指 针 的大小,因 为 数 组 在参数 传递 中弱化 为 指 针 。
逗号操作符除了在 for 语 句中 应 用以外,我没 发现 在哪儿 还 有用 处 。因 为 在一般情况下,逗号改成分号肯定是可以的,在 for 语 句中因 为 分号的作用另有定 义 ,所以不能随便改。 这 才有了逗号的用武之地 。
标题:
:条件操作符
我
觉
得条件操作符的存在就是
为
了
简
化
if-else
语
句。第一,它与
if-else
语
句的功能完全一致;第二,它
虽
然是一行
语
句,但是它
规
定了求解
顺
序,
这
个
顺
序保
证
了有些表达式不被求
值
。
条件操作符是有一定的危 险 性的,危 险 的原因在于它的 优 先 级 特 别 底, 还 容易漏掉括号。它的 优 先 级仅仅 高于 赋值 和逗号运算符,也就是 说 ,只有在与 赋值 或逗号共存 时 ,才可以免去括号,其它情况下都得加上括号。漏加括号的 BUG 是很 难发现 的。
比如 “cout << (i < j) ? i : j;” 这 句的 实际 作用是将表达式 “(i<j)” 的 值输 出,然后 测试 一下 cout 的状 态 ( << 操作符的返回 值 是 cout ),整个表达式的 值 不管是 i 还 是 j ,都被 丢 弃 。
条件操作符是有一定的危 险 性的,危 险 的原因在于它的 优 先 级 特 别 底, 还 容易漏掉括号。它的 优 先 级仅仅 高于 赋值 和逗号运算符,也就是 说 ,只有在与 赋值 或逗号共存 时 ,才可以免去括号,其它情况下都得加上括号。漏加括号的 BUG 是很 难发现 的。
比如 “cout << (i < j) ? i : j;” 这 句的 实际 作用是将表达式 “(i<j)” 的 值输 出,然后 测试 一下 cout 的状 态 ( << 操作符的返回 值 是 cout ),整个表达式的 值 不管是 i 还 是 j ,都被 丢 弃 。
标题:
:箭
头
操作符(
->
)
箭
头
操作符是
C++
发
明的全新操作符,但却不是
C++
才用到的功能。早期的
C
语
言
虽
然没有
类
,却有
结
构体,也允
许
有指向
结
构体
对
象的指
针
。不同的只是没有
发
明
“->”
这
个符号来
进
行
简
化操作。
说
到底,
“->”
的出
现
只是代替原来就可以
实现
的功能。
引用: C++ 语 言 为 包含点操作符和解引用操作符的表达式提供了一个同 义词 :箭 头 操作符( -> )。
笔 记 : 这 一同 义词 的出 现 ,不 仅仅 使程序 简 化而且更易于理解,更重要的是,它降低了出 错 的可能性。出什 么错 呢? 这 就跟操作符的 优 先 级 有 关 了:
p->a();
(*p).a();
以上两行等价,但是第二行却很容易写成 “*p.a();” ,由于点操作符的 优 先 级 高,就成了 “*(p.a());” , 这 里至少包含了两个 错误 :一是 p 不是 对 象,点操作无效;二是 试图对类 成 员 解引用(只有当 该 成 员 返回指 针 才有效)。
也 许 有人要 说 了,第一个 错误 已 经导 致了 编译 不通 过 , 还 要 说 第二个 错误 干什 么 ? 这样 理解就 错 了。 VC++ 为 程序 员 提供了一个 十分 强 大的 库 ,其中有些 类 的 对 象,既可以 进 行点操作也可以 进 行解引用操作的,如果上例中的 p 是那 种类 的 对 象,而且 p.a() 刚 好又返回指 针 ,那 么 上面 这 句将可以通 过编译 ,最 终换 来 难 以 查 找的 BUG 。
记 住,尽量多用箭 头 操作符 。
引用: C++ 语 言 为 包含点操作符和解引用操作符的表达式提供了一个同 义词 :箭 头 操作符( -> )。
笔 记 : 这 一同 义词 的出 现 ,不 仅仅 使程序 简 化而且更易于理解,更重要的是,它降低了出 错 的可能性。出什 么错 呢? 这 就跟操作符的 优 先 级 有 关 了:
p->a();
(*p).a();
以上两行等价,但是第二行却很容易写成 “*p.a();” ,由于点操作符的 优 先 级 高,就成了 “*(p.a());” , 这 里至少包含了两个 错误 :一是 p 不是 对 象,点操作无效;二是 试图对类 成 员 解引用(只有当 该 成 员 返回指 针 才有效)。
也 许 有人要 说 了,第一个 错误 已 经导 致了 编译 不通 过 , 还 要 说 第二个 错误 干什 么 ? 这样 理解就 错 了。 VC++ 为 程序 员 提供了一个 十分 强 大的 库 ,其中有些 类 的 对 象,既可以 进 行点操作也可以 进 行解引用操作的,如果上例中的 p 是那 种类 的 对 象,而且 p.a() 刚 好又返回指 针 ,那 么 上面 这 句将可以通 过编译 ,最 终换 来 难 以 查 找的 BUG 。
记 住,尽量多用箭 头 操作符 。
标题:
:
++
的陷阱
自增和自减符作符是如此常用,以至于没有必要提了。但是任何一本
书
都会下重手来提它,原因是它
虽
然
简单
,却含有玄机。
讲 到前自增和后自增 时 ,几乎所有的 书 都是 这样讲 的:用 “j = i++” 和 “j = ++i” 对 比,告 诉读 者 虽 然 i 都增了 1 ,但是 j 却不一 样 。
这 也没 办 法,因 为绝 大多数 书 在 讲 到 ++ 时还 没有提到 “ 表达式的 值 ” 这 个概念,有些 书 本可能从 头 到尾都不提。 对 于 “j = i++;” 来 说 ,它是一个表达式没 错 ,但是等号右 侧 也是一个表达式, 对 于表达式 “i++” 来 说 ,它是有 值 的, 该 表达式的 值赋 予了 j ,而整个表达式也有一个 值 ,只是 这 个 值 被 丢 弃了。 类 推: “i = j = k = 1;” 语 句中 单 独的 “1” 就是一个表达式,叫常量表达式,它的 值 就是 1 ,它 给 了 k , “k=1” 这 个表达式也有 值 ,它的 值 就是 k 的 值 ,它 给 了 j , …… 最后 给 i 赋值 的表达式也有 值 ,它的 值 就是 i 的 值 , 这 个 值 被 丢 弃。
弄明白了 “ 表达式的 值 ” 后,就可以科学地 讲 解 “++i” 和 “i++” 了,因 为这 两个表达式的 值 是 这样 定 义 的:前置自增 / 减表达式的 值 是修改后的 变 量 值 ,后置 则为 修改前的 值 。
那 么 ,在不 关 心表达式的 值 的 时 候 —— 即只是 给 某 变 量自增或自减一下 —— 究竟用哪个好呢?当然是前置好,因 为 前置只要用一个指令 进 行一下自增自减运算,然后直接返回即可。而后置却要先保存好 这 个初始 值 ,再自增减,然后再返回以前保存的 值 。写 过 操作符重 载 的程序 员应该 更能体会 这 里面的区 别 。
“++i” 或 “i++” 是 这样简洁 ,所以它 们 比 “i = i + 1;” 要美得多,所以 书 上 说 “ 简洁 就是美 ” 。但是,在美的同 时 ,一个美 丽 的陷阱正在招手,程序 员们 必 须 理解并小心。
“if (ia[index++] < ia[index])” 这样 的表达式是危 险 的。前文中已 经 提到了, C++ 中只有极少的几个操作符是 规 定了 求 值顺 序的, “<” 号没有 规 定,那 么 , 这 两个数要比 较 大小,系 统 究竟先求前者 还 是后者?如果先求前者,那 么 求后者的 时 候 index 是 值 有没有增一?
经 常在 论坛 上看到有人 讨论 “j = i++ + i++ + i++;” 的 结 果, 还 有人把自己的 实 践 结 果 贴 出来 证实 自己的理 论 。 这 正是初学者令人悲哀与同情的地方: 对 于 应该 好好掌握的知 识 没有足 够 的 热 情,却把一腔 热 血放在 这 些无 须讨论 的 话题 上, C++ 初学者要走的路不 仅长 ,而且充 满 了 荆 棘 。
讲 到前自增和后自增 时 ,几乎所有的 书 都是 这样讲 的:用 “j = i++” 和 “j = ++i” 对 比,告 诉读 者 虽 然 i 都增了 1 ,但是 j 却不一 样 。
这 也没 办 法,因 为绝 大多数 书 在 讲 到 ++ 时还 没有提到 “ 表达式的 值 ” 这 个概念,有些 书 本可能从 头 到尾都不提。 对 于 “j = i++;” 来 说 ,它是一个表达式没 错 ,但是等号右 侧 也是一个表达式, 对 于表达式 “i++” 来 说 ,它是有 值 的, 该 表达式的 值赋 予了 j ,而整个表达式也有一个 值 ,只是 这 个 值 被 丢 弃了。 类 推: “i = j = k = 1;” 语 句中 单 独的 “1” 就是一个表达式,叫常量表达式,它的 值 就是 1 ,它 给 了 k , “k=1” 这 个表达式也有 值 ,它的 值 就是 k 的 值 ,它 给 了 j , …… 最后 给 i 赋值 的表达式也有 值 ,它的 值 就是 i 的 值 , 这 个 值 被 丢 弃。
弄明白了 “ 表达式的 值 ” 后,就可以科学地 讲 解 “++i” 和 “i++” 了,因 为这 两个表达式的 值 是 这样 定 义 的:前置自增 / 减表达式的 值 是修改后的 变 量 值 ,后置 则为 修改前的 值 。
那 么 ,在不 关 心表达式的 值 的 时 候 —— 即只是 给 某 变 量自增或自减一下 —— 究竟用哪个好呢?当然是前置好,因 为 前置只要用一个指令 进 行一下自增自减运算,然后直接返回即可。而后置却要先保存好 这 个初始 值 ,再自增减,然后再返回以前保存的 值 。写 过 操作符重 载 的程序 员应该 更能体会 这 里面的区 别 。
“++i” 或 “i++” 是 这样简洁 ,所以它 们 比 “i = i + 1;” 要美得多,所以 书 上 说 “ 简洁 就是美 ” 。但是,在美的同 时 ,一个美 丽 的陷阱正在招手,程序 员们 必 须 理解并小心。
“if (ia[index++] < ia[index])” 这样 的表达式是危 险 的。前文中已 经 提到了, C++ 中只有极少的几个操作符是 规 定了 求 值顺 序的, “<” 号没有 规 定,那 么 , 这 两个数要比 较 大小,系 统 究竟先求前者 还 是后者?如果先求前者,那 么 求后者的 时 候 index 是 值 有没有增一?
经 常在 论坛 上看到有人 讨论 “j = i++ + i++ + i++;” 的 结 果, 还 有人把自己的 实 践 结 果 贴 出来 证实 自己的理 论 。 这 正是初学者令人悲哀与同情的地方: 对 于 应该 好好掌握的知 识 没有足 够 的 热 情,却把一腔 热 血放在 这 些无 须讨论 的 话题 上, C++ 初学者要走的路不 仅长 ,而且充 满 了 荆 棘 。
标题:
:回
忆
十几年前的
编
程入
门
本来不想
为这
段写
读书
笔
记
,不
过
突然想起十几年前的一件趣事来,
还
是
记
下来吧。
1993 年的 时 候,学校 开设 了 “ 劳 技 ” 课 , 讲 的是 BASIC 语 言。 对 于当 时连电脑 都没看 过 一眼的我 们 来 说 ,学校 开设这样 的 课 ,真是 让 我 们 无比感 动 。我至今仍然感 谢 我的母校,在片面追求升学率、大量 缩 减副 课 的全局下,我的母校居然 开设 了音 乐 、美 术 、 劳 技等一系列副之又副的 课 。 这让 我至今 难 忘。而令一方面,我今天能 够 在程序界打拼,完全是从那 时 候 开 始陪 养 的 兴 趣。如果我的高中没有 开设这门课 ,我未必就不 进 入程序界,但是入 门 至少要 晚 三年。
这 三年, 我学的 编 程 东 西少之又少, 对 于整个 BASIC 来 说 , 简 直 连 皮毛都不如,而且学校只提供了一次上机机会, 练 的是 开 机与 关 机。我所 谓 的 “ 调试 程序 ” 是在自己的小霸王学 习 机上 进 行的。但是,在 电脑 没有普及的年份,懂得一点点就是很先 进 的了。 进 入大学后,堂堂一个大学的班 级 ,居然只有两个人碰 过电脑 ,大家的基 础 可想而知。在同学 们 拼命学 习 DOS 命令的 时 候,我已 经 遥遥地走在了前面。之后学 习 True Basic 自然一日千里,之后自学 VB 、自学 C 也就 顺 理成章。如果我的高中没有 开设这门课 ,我未必就不 进 入程序界,但是我在 进 大学的 头 一段 时间 肯定会 与其他同学一起拼命 记 DOS 命令。要知道,其他同学至所以学得比我慢,并不是比我笨,而是没有 习惯电脑 的思 维 模式。
该说 “ 赋值 操作符 ” 了。高中 开设 的 BASIC 语 言 课 ,其 课 本是不到半厘米厚的小 书 。但是我 爱 不 释 手地提前 阅读 了。 读 得一知半解就去做后面的 习题 , 发现 一句 “P=P+1” ,心想: 这 怎 么 可能嘛? P 怎 么 可能等于 P 加一呢?移 项 一减,不就相当于 0=1 了 吗 ?一定是 书 上印 错 了。于是,我将其中一个 P 改成了 R ,而且是用黑 钢 笔描的。我描得是如此 细 致,以至于根本看不出那个 R 是 P 改的。等到老 师讲 到 这 一 节 的 时 候,我 虽 然已 经 懂了 这 里 的 “=” 与数学上的 “=” 不一 样 ,但是由于把 P 看成了 R , 这题还 是做 错 了。
当 时 的情况是 这样 的:老 师 喊了几个同学上去做 题 ,没有一个会。老 师说 “ 有没有 谁 会做的?主 动 上来? ” 好几个同学立即喊我的名字,把我逼上去了。 结 果我 这么 一做就做 错 了。老 师 表 扬 了我的勇气,但是同学却 说 我 虽 然平 时 捧着 这 本 书 不放,原来也是个 “ 菜 鸟 ” 。
赋值 操作符就是在那个 时 候 给 我留下深刻印像的,那天我知道了, “=” 号不表示左右相等。
复 合 赋值 操作符也比 较简单 ,理解了它的用法就好了。 C++ 至所以有了 赋值 操作符以后 还 要 复 合 赋值 操作符,不 仅仅 是 为 了 简 化代 码 , 还 可以加快 处 理速度。 “i=i+j” 和 “i+=j” 相比,前者的 i 求 值 了两次。不 过 , 这 点性能差 别对 整个程序性能来 说 不大。 还 有一个区 别 就是 优 先 级 方面的了。 “i*=j+1” 如果不写成 复 合 赋值 ,就要加上括号: i=i*(j+1) 。
1993 年的 时 候,学校 开设 了 “ 劳 技 ” 课 , 讲 的是 BASIC 语 言。 对 于当 时连电脑 都没看 过 一眼的我 们 来 说 ,学校 开设这样 的 课 ,真是 让 我 们 无比感 动 。我至今仍然感 谢 我的母校,在片面追求升学率、大量 缩 减副 课 的全局下,我的母校居然 开设 了音 乐 、美 术 、 劳 技等一系列副之又副的 课 。 这让 我至今 难 忘。而令一方面,我今天能 够 在程序界打拼,完全是从那 时 候 开 始陪 养 的 兴 趣。如果我的高中没有 开设这门课 ,我未必就不 进 入程序界,但是入 门 至少要 晚 三年。
这 三年, 我学的 编 程 东 西少之又少, 对 于整个 BASIC 来 说 , 简 直 连 皮毛都不如,而且学校只提供了一次上机机会, 练 的是 开 机与 关 机。我所 谓 的 “ 调试 程序 ” 是在自己的小霸王学 习 机上 进 行的。但是,在 电脑 没有普及的年份,懂得一点点就是很先 进 的了。 进 入大学后,堂堂一个大学的班 级 ,居然只有两个人碰 过电脑 ,大家的基 础 可想而知。在同学 们 拼命学 习 DOS 命令的 时 候,我已 经 遥遥地走在了前面。之后学 习 True Basic 自然一日千里,之后自学 VB 、自学 C 也就 顺 理成章。如果我的高中没有 开设这门课 ,我未必就不 进 入程序界,但是我在 进 大学的 头 一段 时间 肯定会 与其他同学一起拼命 记 DOS 命令。要知道,其他同学至所以学得比我慢,并不是比我笨,而是没有 习惯电脑 的思 维 模式。
该说 “ 赋值 操作符 ” 了。高中 开设 的 BASIC 语 言 课 ,其 课 本是不到半厘米厚的小 书 。但是我 爱 不 释 手地提前 阅读 了。 读 得一知半解就去做后面的 习题 , 发现 一句 “P=P+1” ,心想: 这 怎 么 可能嘛? P 怎 么 可能等于 P 加一呢?移 项 一减,不就相当于 0=1 了 吗 ?一定是 书 上印 错 了。于是,我将其中一个 P 改成了 R ,而且是用黑 钢 笔描的。我描得是如此 细 致,以至于根本看不出那个 R 是 P 改的。等到老 师讲 到 这 一 节 的 时 候,我 虽 然已 经 懂了 这 里 的 “=” 与数学上的 “=” 不一 样 ,但是由于把 P 看成了 R , 这题还 是做 错 了。
当 时 的情况是 这样 的:老 师 喊了几个同学上去做 题 ,没有一个会。老 师说 “ 有没有 谁 会做的?主 动 上来? ” 好几个同学立即喊我的名字,把我逼上去了。 结 果我 这么 一做就做 错 了。老 师 表 扬 了我的勇气,但是同学却 说 我 虽 然平 时 捧着 这 本 书 不放,原来也是个 “ 菜 鸟 ” 。
赋值 操作符就是在那个 时 候 给 我留下深刻印像的,那天我知道了, “=” 号不表示左右相等。
复 合 赋值 操作符也比 较简单 ,理解了它的用法就好了。 C++ 至所以有了 赋值 操作符以后 还 要 复 合 赋值 操作符,不 仅仅 是 为 了 简 化代 码 , 还 可以加快 处 理速度。 “i=i+j” 和 “i+=j” 相比,前者的 i 求 值 了两次。不 过 , 这 点性能差 别对 整个程序性能来 说 不大。 还 有一个区 别 就是 优 先 级 方面的了。 “i*=j+1” 如果不写成 复 合 赋值 ,就要加上括号: i=i*(j+1) 。
标题:
:
关
系、
逻辑
和位操作符
关
系操作符本身没什
么
好提的,它
们
与我
们
平
时
身
边
的
逻辑
一
样
,所以不
难
理解。有两点可以略提一下:
一、因 为 ASC 字符中没有 “≥” 这 些符号,所以只好用 “>=” 代替。于是 产 生了 BASIC 和 C++ 的两 种 不同符号集: BASIC 用 “<>” 表示不等于, C++ 则 用 “!=” 。
二、程序 设计时 不能用 “if (i < j < k)” 这样 的写法。原因很 简单 ,因 为这种 写法另有含 义 ,或者 说 正因 为 不能 这样 写,才 给这种 写法另外 赋 了一 种 含 义 。
BASIC 和 C++ 的 逻辑 操作符也有完全不同的写法, BASIC 用比 较 直 观 的 关键 字 “And” 、 “Or” 之 类 , C++ 则 用 “&&” 和 “||” 。 这 也没什 么 , 记 住就行了。
逻辑 操作符中的 “&&” 和 “||” 是 C++ 标 准中 为 数不多的指定了求 值顺 序的操作符(除此以外 还 有条件操作符 “?:” 和逗号操作符 “,” , 关 于求 值顺 序,后面 还 将提及)。 这样 的 规 定惟一的缺点是需要 额 外的 记忆 , 优 点 则 是很明 显 的:它可以 让 “ 危 险 ” 的操作 变 得不危 险 。如 “while (i<MaxSize && Array[i]>0)” , 对 于 “Array[i]” 来 说 ,指 针 越界是很可怕的,但是 “&&” 操作符的求 值顺 序保 证 了指 针 不越界。从另一方面 说 ,要保 证 指 针 不越界,就必 须记 住 该 操作符的求 值顺 序(不然就只能分成两个 语 句写 喽 )。
又要提到 bool 值 的比 较 了, “if (i < j < k)” 的 实际 就是 进 行了 bool 值 的比 较 。本 书 在 讲 解的 时 候, 虽 然提到了 “ 将 k 与整数 0 或 1 做比 较 ” ,但是我宁可提醒大家不去 记 住 这 个。 记 住 bool 值 只有 true 和 false 两个 值 ,比 记 住 1 和 0 要好得多,因 为虽 然 false 就是 0 ,但是 true 却不 仅 是 1 。
C++ 中如果只有 逻辑 操作符也就算了,它偏 偏 还 有 “&” 和 “|” 这样 的位操作符。而位操作正是 BASIC 不提供的功能。 这 就 给 初学 C/C++ 的人 带 来了 难 度。 难 点不在于理解,而在于 记忆 。位操作符的作用是 进 行某一 Bit 位的 设 定,在一个字 节掰 成八份用的年代,它非常常用, — 比如 Turbo C 中的屏幕 设 置,就是 3 位表示背景色、 4 位表示前景色、一位表示 闪烁 ,用一个字 节 完全存放了屏幕字体的信息。 现 在的海量存 储 与高速 处 理中,大可不必 这么节约 了(从 处 理速上看,非但没有 节约 ,反而浪 费 了),所以,不会位操作也没什 么 大不了的。
不 过 ,不熟悉位操作的用 户 却都知道 “<<” 和 “>>” 的另一用 处 。 这 事不能怪程序 员 ,几乎所有的 C++ 书 本都会从 “cin >> i;” 和 “cout << i;” 入手。不用知道 这 两个操作符原来是干什 么 的,甚至不用知道 “ 重 载 ” 是怎 么 回事。 C++ 来到 这 个世界, 发 展了 C 语 言,使得 “ 知其然不知道其所以然 ” 的程序 员 也能好好工作。 也 许这 是它的一个 进步 吧 。
一、因 为 ASC 字符中没有 “≥” 这 些符号,所以只好用 “>=” 代替。于是 产 生了 BASIC 和 C++ 的两 种 不同符号集: BASIC 用 “<>” 表示不等于, C++ 则 用 “!=” 。
二、程序 设计时 不能用 “if (i < j < k)” 这样 的写法。原因很 简单 ,因 为这种 写法另有含 义 ,或者 说 正因 为 不能 这样 写,才 给这种 写法另外 赋 了一 种 含 义 。
BASIC 和 C++ 的 逻辑 操作符也有完全不同的写法, BASIC 用比 较 直 观 的 关键 字 “And” 、 “Or” 之 类 , C++ 则 用 “&&” 和 “||” 。 这 也没什 么 , 记 住就行了。
逻辑 操作符中的 “&&” 和 “||” 是 C++ 标 准中 为 数不多的指定了求 值顺 序的操作符(除此以外 还 有条件操作符 “?:” 和逗号操作符 “,” , 关 于求 值顺 序,后面 还 将提及)。 这样 的 规 定惟一的缺点是需要 额 外的 记忆 , 优 点 则 是很明 显 的:它可以 让 “ 危 险 ” 的操作 变 得不危 险 。如 “while (i<MaxSize && Array[i]>0)” , 对 于 “Array[i]” 来 说 ,指 针 越界是很可怕的,但是 “&&” 操作符的求 值顺 序保 证 了指 针 不越界。从另一方面 说 ,要保 证 指 针 不越界,就必 须记 住 该 操作符的求 值顺 序(不然就只能分成两个 语 句写 喽 )。
又要提到 bool 值 的比 较 了, “if (i < j < k)” 的 实际 就是 进 行了 bool 值 的比 较 。本 书 在 讲 解的 时 候, 虽 然提到了 “ 将 k 与整数 0 或 1 做比 较 ” ,但是我宁可提醒大家不去 记 住 这 个。 记 住 bool 值 只有 true 和 false 两个 值 ,比 记 住 1 和 0 要好得多,因 为虽 然 false 就是 0 ,但是 true 却不 仅 是 1 。
C++ 中如果只有 逻辑 操作符也就算了,它偏 偏 还 有 “&” 和 “|” 这样 的位操作符。而位操作正是 BASIC 不提供的功能。 这 就 给 初学 C/C++ 的人 带 来了 难 度。 难 点不在于理解,而在于 记忆 。位操作符的作用是 进 行某一 Bit 位的 设 定,在一个字 节掰 成八份用的年代,它非常常用, — 比如 Turbo C 中的屏幕 设 置,就是 3 位表示背景色、 4 位表示前景色、一位表示 闪烁 ,用一个字 节 完全存放了屏幕字体的信息。 现 在的海量存 储 与高速 处 理中,大可不必 这么节约 了(从 处 理速上看,非但没有 节约 ,反而浪 费 了),所以,不会位操作也没什 么 大不了的。
不 过 ,不熟悉位操作的用 户 却都知道 “<<” 和 “>>” 的另一用 处 。 这 事不能怪程序 员 ,几乎所有的 C++ 书 本都会从 “cin >> i;” 和 “cout << i;” 入手。不用知道 这 两个操作符原来是干什 么 的,甚至不用知道 “ 重 载 ” 是怎 么 回事。 C++ 来到 这 个世界, 发 展了 C 语 言,使得 “ 知其然不知道其所以然 ” 的程序 员 也能好好工作。 也 许这 是它的一个 进步 吧 。
标题:
:可
爱
的算
术
操作符
算
术
操作符是最容易理解的符号了,因
这
它与平
时
做的数学是完全相同的
规则
。就
连
小学的知
识
“
先乘除后加减
”
都完全适用。
不 过 ,就像因 为 与生活的 逻辑 完全一 样导 致易于理解一般,与生活 逻辑 不一致的 问题 就比 较难 以理解了。比如 “ 有 9 个苹果, 3 个小朋友分,平均 每 个小朋友可以分到几个苹果? ” , 现实 中既不可能是 -9 个苹果,也不可能 给 -3 个小朋友分。而是 C/C++ 中的除法和求余却不得不面 对这种 情况。它 们 非但 难 于理解,而且 还 有不确定因素。
引用:如果两个操作数都 为 正,除法和求模操作的 结 果也是正数(或零);如果两个操作数 都是 负 数,除法操作的 结 果 为 正数(或零),而求模操作的 结 果 则为负 数(或零);如果只有一个操作数是 负 数, 这 两 种 操作的 结 果取决于机器;求模 结 果的符号也取决于机器,而除法操作的 值则 是 负 数(或零)。
笔 记 :以上 这 一大堆似乎有些 费 口舌。 这么说 吧,如果两个数同号, 则 一切都那 么简单 。即使是两个 负 数相除,只要把它 们 当成两个正数就可以了 —— 商肯定是正的,余数只要 对应调 整一下符号即可;如果是一正一 负 两个数相除,那 么 商肯定是 负 的,但是究竟是 负 多少可不一定,余数就更 难说 了, 连 是正是 负 都不能确定。
21 % 6 = 3;
21 % 7 = 0;
-21 % -8 = -5;
21 % -5 = ?;// 与机器相 关 ,可能是 1 可能是 -4
21 / 6 = 3;
21 / 3 = 3;
-21 / -28 = 2;
21 / -5 = ?;// 与机器相 关 ,可能是 -4 或 -5
引用:当只有一个操作数 为负 数 时 ,求模操作 结 果 值 的符号可依据分子(被除数)或分母(除数)的符号而定。如果求模的 结 果随分子的符号, 则 除出来的 值 向零一 侧 取整;如果求模与分母的符号匹配, 则 除出来的 值 向 负 无 穷 一 侧 取整。
10 个苹果分 给 -3 个朋友,平均 每 个小朋友可以分到几个 ? 还 剩下几个 ?
不 过 ,就像因 为 与生活的 逻辑 完全一 样导 致易于理解一般,与生活 逻辑 不一致的 问题 就比 较难 以理解了。比如 “ 有 9 个苹果, 3 个小朋友分,平均 每 个小朋友可以分到几个苹果? ” , 现实 中既不可能是 -9 个苹果,也不可能 给 -3 个小朋友分。而是 C/C++ 中的除法和求余却不得不面 对这种 情况。它 们 非但 难 于理解,而且 还 有不确定因素。
引用:如果两个操作数都 为 正,除法和求模操作的 结 果也是正数(或零);如果两个操作数 都是 负 数,除法操作的 结 果 为 正数(或零),而求模操作的 结 果 则为负 数(或零);如果只有一个操作数是 负 数, 这 两 种 操作的 结 果取决于机器;求模 结 果的符号也取决于机器,而除法操作的 值则 是 负 数(或零)。
笔 记 :以上 这 一大堆似乎有些 费 口舌。 这么说 吧,如果两个数同号, 则 一切都那 么简单 。即使是两个 负 数相除,只要把它 们 当成两个正数就可以了 —— 商肯定是正的,余数只要 对应调 整一下符号即可;如果是一正一 负 两个数相除,那 么 商肯定是 负 的,但是究竟是 负 多少可不一定,余数就更 难说 了, 连 是正是 负 都不能确定。
21 % 6 = 3;
21 % 7 = 0;
-21 % -8 = -5;
21 % -5 = ?;// 与机器相 关 ,可能是 1 可能是 -4
21 / 6 = 3;
21 / 3 = 3;
-21 / -28 = 2;
21 / -5 = ?;// 与机器相 关 ,可能是 -4 或 -5
引用:当只有一个操作数 为负 数 时 ,求模操作 结 果 值 的符号可依据分子(被除数)或分母(除数)的符号而定。如果求模的 结 果随分子的符号, 则 除出来的 值 向零一 侧 取整;如果求模与分母的符号匹配, 则 除出来的 值 向 负 无 穷 一 侧 取整。
10 个苹果分 给 -3 个朋友,平均 每 个小朋友可以分到几个 ? 还 剩下几个 ?
标题:
:操作符
第五章
开
始了,看得出来,从
这
章才真正
开
始
讲
解
C++
的基本内容。没有
这
里的内容,前四章都是狗屎。
操作符就是我 们 平 时 理解的 “ 运算符 ” 了,不 过 因 为 C++ 是 计 算机 语 言,它与我 们 平 时 生活有着不一 样 的 逻辑 ,所以,在我 们 平 时 看来 简单 的 “3+4” ,到了 C++ 里,就得分成一个操作符和两个操作数了。
操作符的含 义 以及它能得到的 结 果,不 仅仅 取决于操作符本身, 还 同 时 取决于操作数。当初学 C 语 言的 时 候, 发现 做除法要用 “10/3.0” 而不用 “10/3” 着 实 惊 讶 了一回。特 别 是从 BASIC 走 过 来的人, BASIC 里没有 这么强 的 类 型,所以 10/3 就是浮点数,要整除 还 得用 “Int()” 函数或改用 “/” 运算符(不是 每 个 VB 程序 员 都知道 这 个运算符的哦。)
从 C/C++ 中弄明白了 “10/3.0” 和 “10/3” ,反 过 来再去理解 C 与 BASIC 的区 别 ,不 难发现 C/C++ 这样 做的确比 BASIC 高明得多。而 进 一 步 了解了硬件的运算机制后, 则 可以理解 这样 做不 仅仅 是高明,而且是必 须 。
引用:有些符号既可以表示一元操作也可以表示二元操作。例如 *…… , 这种 两用法相互独立 、各不相 关 ,如果将其 视为 两个不同的符号可能会更容易理解些。 …… 需要根据 该 符号所 处 的上下文来确定它代表一元操作 还 是二元操作。
笔 记 : 这 是一个大家都明白,但是大家都不会去想的 问题 。 细 想起来,我 们 不去想,正是因 为 我 们 早已熟知。然而我 们读 程序可以上下 关联 , 计 算机要做到 这 点就不容易 —— 比如拼音 输 入法的自 动选词 。由此看来, C++ 编译 器是十分 优 秀的人工智能 软 件 。
操作符就是我 们 平 时 理解的 “ 运算符 ” 了,不 过 因 为 C++ 是 计 算机 语 言,它与我 们 平 时 生活有着不一 样 的 逻辑 ,所以,在我 们 平 时 看来 简单 的 “3+4” ,到了 C++ 里,就得分成一个操作符和两个操作数了。
操作符的含 义 以及它能得到的 结 果,不 仅仅 取决于操作符本身, 还 同 时 取决于操作数。当初学 C 语 言的 时 候, 发现 做除法要用 “10/3.0” 而不用 “10/3” 着 实 惊 讶 了一回。特 别 是从 BASIC 走 过 来的人, BASIC 里没有 这么强 的 类 型,所以 10/3 就是浮点数,要整除 还 得用 “Int()” 函数或改用 “/” 运算符(不是 每 个 VB 程序 员 都知道 这 个运算符的哦。)
从 C/C++ 中弄明白了 “10/3.0” 和 “10/3” ,反 过 来再去理解 C 与 BASIC 的区 别 ,不 难发现 C/C++ 这样 做的确比 BASIC 高明得多。而 进 一 步 了解了硬件的运算机制后, 则 可以理解 这样 做不 仅仅 是高明,而且是必 须 。
引用:有些符号既可以表示一元操作也可以表示二元操作。例如 *…… , 这种 两用法相互独立 、各不相 关 ,如果将其 视为 两个不同的符号可能会更容易理解些。 …… 需要根据 该 符号所 处 的上下文来确定它代表一元操作 还 是二元操作。
笔 记 : 这 是一个大家都明白,但是大家都不会去想的 问题 。 细 想起来,我 们 不去想,正是因 为 我 们 早已熟知。然而我 们读 程序可以上下 关联 , 计 算机要做到 这 点就不容易 —— 比如拼音 输 入法的自 动选词 。由此看来, C++ 编译 器是十分 优 秀的人工智能 软 件 。
标题:
:多
维
数
组
引用:
严
格地
说
,
C++
中没有多
维
数
组
。
笔 记 :不只是 C++ 啦, C 中就是 这样 。不 过 ,正因 为 C++ 中没有多 维 数 组 ,而提供了 “ 数 组 的数 组 ” ,所以 C/C++ 在数 组 使用上更灵活。
多 维 数 组 的定 义 和使用没什 么 要多提的,用 过 就懂了。无非是多一 对 括号而已。不 过 ,如果把它跟指 针 一起用,倒是要注意的:二 维 数 组 名 对应 的是 “ 指向指 针 的指 针 ” ,所以,如果要在函数 间传递 多 维 数 组 ,指 针类 型一定要正确:
int a[3][4];
int *p1 = &a[0][0];//a[][] 是一个 int , 对 其取地址就是 int*
int *p2 = a[0];//a[0] 虽 然是 a 有一个元素,但它也是另一个数 组 的数 组 名
int **p3 = a;//a 是一个二 维 数 组 的数 组 名
int **p4 = &a[0];//a[0] 是一个数 组 名,它是 a 数 组 的一个成 员
另外,有一个比 较难记 、容易混淆的用法:
int (*p5)[4] = a;
说 它容易混淆,是因 为 它与 “int *p5[4];” 有着截然不同的意 义 。前者是指定 义 一个指向数 组 的指 针 ,后者 则 是定 义 一个指 针 数 组 。 —— 头 昏 ing...
本人在 实际 使用中, 经 常避 开 多 维 数 组 ,而用其它途径来使用一大堆数 值 。比如可以 这样 用:
int a, b;// 两 维 的元素个数
int *p = new int[a*b];
for (int i=0; i<a; ++i)
for (int j=0; j<b; ++j)
p[i*a+j].....;
delete []p;
用 这种 方法,就是三 维 、四 维 也不用考 虑 “ 指向指 针 的指 针 ” 这么复杂 的 东 西。 不是我不会,而是不高 兴 去想 。
笔 记 :不只是 C++ 啦, C 中就是 这样 。不 过 ,正因 为 C++ 中没有多 维 数 组 ,而提供了 “ 数 组 的数 组 ” ,所以 C/C++ 在数 组 使用上更灵活。
多 维 数 组 的定 义 和使用没什 么 要多提的,用 过 就懂了。无非是多一 对 括号而已。不 过 ,如果把它跟指 针 一起用,倒是要注意的:二 维 数 组 名 对应 的是 “ 指向指 针 的指 针 ” ,所以,如果要在函数 间传递 多 维 数 组 ,指 针类 型一定要正确:
int a[3][4];
int *p1 = &a[0][0];//a[][] 是一个 int , 对 其取地址就是 int*
int *p2 = a[0];//a[0] 虽 然是 a 有一个元素,但它也是另一个数 组 的数 组 名
int **p3 = a;//a 是一个二 维 数 组 的数 组 名
int **p4 = &a[0];//a[0] 是一个数 组 名,它是 a 数 组 的一个成 员
另外,有一个比 较难记 、容易混淆的用法:
int (*p5)[4] = a;
说 它容易混淆,是因 为 它与 “int *p5[4];” 有着截然不同的意 义 。前者是指定 义 一个指向数 组 的指 针 ,后者 则 是定 义 一个指 针 数 组 。 —— 头 昏 ing...
本人在 实际 使用中, 经 常避 开 多 维 数 组 ,而用其它途径来使用一大堆数 值 。比如可以 这样 用:
int a, b;// 两 维 的元素个数
int *p = new int[a*b];
for (int i=0; i<a; ++i)
for (int j=0; j<b; ++j)
p[i*a+j].....;
delete []p;
用 这种 方法,就是三 维 、四 维 也不用考 虑 “ 指向指 针 的指 针 ” 这么复杂 的 东 西。 不是我不会,而是不高 兴 去想 。
标题:
:
动态
数
组
我
晕
,本
书
才
讲
了个
开头
,居然
讲
到
new
和
delete
了。我
“
偷窥
”
了一下:下一章
开
始才
讲
到操作符,而且下章将有
专门
的一
节讲
new
和
delete
。看来
这
里提到它
们
的目的只是
为
了
说
明
string
这样
的
类为
什
么
可以自
动
适
应
大小。
new 的返回 值 是一个指 针 ,不 过 本 书暂时 没有提到 new 也会返回 NULL 的。是的, 暂时还 不用提内容不 够这么复杂 的情况。
引用:在自由存 储 区中 创 建的数 组对 象数 组 是没有名字的,程序 员 只能通 过 其地址 间 接地 访问 堆中的 对 象。
笔 记 : 这 里有两个 问题 ,一是 “ 数 组 的名字 ” 其 实 也是个指 针 ,指 针 当然也可以看成 “ 数 组 的名字 ” , 这 本来就可以互 换 的,正如我前面 说 的一 样 : “5[a]” 完全等价于 “*(5+a)” 。至于 a 是静 态 的数 组 名 还 是 动态 的指 针 ,没有区 别 。第二个 问题 是 这 里提到了 “ 堆 ” ,在没有 讲 解内存之前, 这样说毕 竟理解上有 难 度。 还 是那句 话 , 这 本 书 是 给 有一定基 础 的人看的。
令我耳目一新的是: new 还 能 创 建 const 数 组 。 —— 不是我不懂,而是 实 在没想到。再 说 了, 创 建一大堆 const 的内容,而且只能初始化 为 同一个 值 。那 这 有什 么 用?正如本 书 提到的一 样 : “ 这样 的数 组实际 上用 处 不大 ” 。依我看,不是用 处 不大,而是根本没用。
动态 空 间 的 释 放 应该 用 “delete []p;” 而不是 “delete p;” , 这 是一个只要 记 住就可以的 问题 ,我之所以提上一句,是因 为 有人没有注意 过这 个 细节 ,包括很多自以 为 很了不起的程序 员 。
引用:如果 遗 漏了方括号 对 , 这 是一个 编译 器无法 发现 的 错误 ,将 导 致程序在运行 时 出 错 。
new 的返回 值 是一个指 针 ,不 过 本 书暂时 没有提到 new 也会返回 NULL 的。是的, 暂时还 不用提内容不 够这么复杂 的情况。
引用:在自由存 储 区中 创 建的数 组对 象数 组 是没有名字的,程序 员 只能通 过 其地址 间 接地 访问 堆中的 对 象。
笔 记 : 这 里有两个 问题 ,一是 “ 数 组 的名字 ” 其 实 也是个指 针 ,指 针 当然也可以看成 “ 数 组 的名字 ” , 这 本来就可以互 换 的,正如我前面 说 的一 样 : “5[a]” 完全等价于 “*(5+a)” 。至于 a 是静 态 的数 组 名 还 是 动态 的指 针 ,没有区 别 。第二个 问题 是 这 里提到了 “ 堆 ” ,在没有 讲 解内存之前, 这样说毕 竟理解上有 难 度。 还 是那句 话 , 这 本 书 是 给 有一定基 础 的人看的。
令我耳目一新的是: new 还 能 创 建 const 数 组 。 —— 不是我不懂,而是 实 在没想到。再 说 了, 创 建一大堆 const 的内容,而且只能初始化 为 同一个 值 。那 这 有什 么 用?正如本 书 提到的一 样 : “ 这样 的数 组实际 上用 处 不大 ” 。依我看,不是用 处 不大,而是根本没用。
动态 空 间 的 释 放 应该 用 “delete []p;” 而不是 “delete p;” , 这 是一个只要 记 住就可以的 问题 ,我之所以提上一句,是因 为 有人没有注意 过这 个 细节 ,包括很多自以 为 很了不起的程序 员 。
引用:如果 遗 漏了方括号 对 , 这 是一个 编译 器无法 发现 的 错误 ,将 导 致程序在运行 时 出 错 。
标题:
:
C
风
格字符
串
给这
篇文章定下
这
个
标题:
,是因
为书
中就是
这样说
的。本
书
是
讲
解
C++
的,所以它推荐
读
者尽量使用
C++
的内容,而
实际
上像我
这样
从
C
过
来的人,
还
是
习惯
于使用
C
风
格的字符串。
——
我又想起了那句
话
:
“
原来我只是一个
‘
古代
’
的
C++
程序
员
。
”(
见
《数
组
》一文
)
C 语 言是用字符数 组 来做字符串的(当然 这 个字符数 组 必需要有一个 NULL 结 尾),因 为 字符串是如此常用, C 语 言 还专门开发 了一套 库 函数来 处 理 这 个特殊的数 组 。于是,我 们进 行字符串操作 时 ,可以忘 记 指 针 、忘 记 循 环 、 还 可以忘 记 char 这 个内置 类 型。
正是因 为 如此,林 锐 博士的《高 质 量 C++/C 编 程指南》中 还 特 别强调 ,不可以用指 针 的 赋值 和比 较 来 进 行字符串的 赋值 和比 较 。 这 个警告 对 于从 VB 转过 来的人尤其重要。
使用 C 风 格的字符串有两点是必 须 保 证 的:一是要 给这 个数 组开 劈足 够长 度的空 间 ;二是一定不要忘了 NULL ;其中第二点一般程序 员 不会犯 错 ,因 为毕 竟没几个人用 “chat s[3] = {'a', 'b', '/0'}” 这种 方式来定 义 字符串。第一点就成了重中之重。我 们 在 strcpy 之前,有没有考 虑过 目 标 字符串可能的空 间 不足?
“strn” 风 格的函数既救了大家也可能害了大家, 说 它救了大家,因 为 大家在 strncpy 和 strncat 时 可以控制字符个数,即使源字符串太 长 ,也可以避免内存溢出。但是它存在的危 险 性是它不会 为 目 标 字符串添加 NULL 。
所以, 书 写到 这 里再次做了一个提醒: “ 尽可能使用 标 准 库类 型 string”—— 我都忘了 这 是第几次提醒了,本 书 一而再再而三地提醒 读 者不要做 “ 古代 ” 的 C++ 程序 员 。
C 语 言是用字符数 组 来做字符串的(当然 这 个字符数 组 必需要有一个 NULL 结 尾),因 为 字符串是如此常用, C 语 言 还专门开发 了一套 库 函数来 处 理 这 个特殊的数 组 。于是,我 们进 行字符串操作 时 ,可以忘 记 指 针 、忘 记 循 环 、 还 可以忘 记 char 这 个内置 类 型。
正是因 为 如此,林 锐 博士的《高 质 量 C++/C 编 程指南》中 还 特 别强调 ,不可以用指 针 的 赋值 和比 较 来 进 行字符串的 赋值 和比 较 。 这 个警告 对 于从 VB 转过 来的人尤其重要。
使用 C 风 格的字符串有两点是必 须 保 证 的:一是要 给这 个数 组开 劈足 够长 度的空 间 ;二是一定不要忘了 NULL ;其中第二点一般程序 员 不会犯 错 ,因 为毕 竟没几个人用 “chat s[3] = {'a', 'b', '/0'}” 这种 方式来定 义 字符串。第一点就成了重中之重。我 们 在 strcpy 之前,有没有考 虑过 目 标 字符串可能的空 间 不足?
“strn” 风 格的函数既救了大家也可能害了大家, 说 它救了大家,因 为 大家在 strncpy 和 strncat 时 可以控制字符个数,即使源字符串太 长 ,也可以避免内存溢出。但是它存在的危 险 性是它不会 为 目 标 字符串添加 NULL 。
所以, 书 写到 这 里再次做了一个提醒: “ 尽可能使用 标 准 库类 型 string”—— 我都忘了 这 是第几次提醒了,本 书 一而再再而三地提醒 读 者不要做 “ 古代 ” 的 C++ 程序 员 。
标题:
:指
针
(三)指
针
与数
组
指
针
和数
组
之
间
是什
么关
系呢?
书
中曰
“
密切相
关
”
。其
实
,那
简
真就是同一回事嘛。用到指
针
的
时
候,你未必会用到数
组
;但是只要你用到数
组
,你就必要然用到指
针
(即使你不知道)。
正是因 为 指 针 可以用加或减运算来移 动 它所指的位置,而且 每 加一或减一正好移 动 到相 邻 一个同 类 型的 变 量(不管 这 个 变 量占内存是多少),那 么 我有意将一堆同 类 型的 变 量放在一起,拿一个指 针 指向它 们 中的第一个,再 记 住它 们 的个数, 这 就成了数 组 。
数 组 用一 组 方括号来解引用其中的某个成 员 , 这 也只是指 针 运算的 简 化。比如:
int a[10];
a[5] = 5;
以上 这种 代 码谁 都用 过 , 谁 都能理解。那 么 下面 这 行代 码 呢?
5[a] = 5;
这种 用法恐怕很少有人知道,即使 现 在知道了,恐怕也很 难 理解。 实际 上知道了数 组 运算的 实质 , 这 行代 码 的迷 雾 就会立即消失: C/C++ 语 言 处 理括号的方法很 简单 ,将方括号前面的 值 和方括号内的 值 相加,得到一个新的指 针 ,再取指 针 所指的 对 象 值 。 “a[5]” 就完全等价于 “*(a+5)” , “5[a]” 就完全等价于 “*(5+a)” 。
那 么 “*(a+5)” 是什 么 运算呢?指 针 运算。因 为 在 编译 器 处 理 “int a[10];” 的 时 候,就等于定 义 了一个 “int * const a;” 同 时 将它初始化 为 指向 栈 内存的某 处 。
实际 上,正是因 为 “*(a+5)” 这种 用法 实 在太常用了, C 才 规 定了它的替代用法,后来 这 个替代用法被广 为 接受,而它的 实际 却被人 遗 忘。
以上内容本 书 未有提及, 这 是我看 书 看到 这 里的一点心得,作 为读书 笔 记 写下来。不是 为 了炫耀。本 书虽 然是 给 有一定基 础 的人 读 的,但是 毕 竟它只是按 步 就章地写下 C++ 的 语 法 规则 ,没有必要提及 这 些 技巧性高而又 实 用性少的内容。我之所以要写下来,目的是 为 了便于理解指 针 运算 。
正是因 为 指 针 可以用加或减运算来移 动 它所指的位置,而且 每 加一或减一正好移 动 到相 邻 一个同 类 型的 变 量(不管 这 个 变 量占内存是多少),那 么 我有意将一堆同 类 型的 变 量放在一起,拿一个指 针 指向它 们 中的第一个,再 记 住它 们 的个数, 这 就成了数 组 。
数 组 用一 组 方括号来解引用其中的某个成 员 , 这 也只是指 针 运算的 简 化。比如:
int a[10];
a[5] = 5;
以上 这种 代 码谁 都用 过 , 谁 都能理解。那 么 下面 这 行代 码 呢?
5[a] = 5;
这种 用法恐怕很少有人知道,即使 现 在知道了,恐怕也很 难 理解。 实际 上知道了数 组 运算的 实质 , 这 行代 码 的迷 雾 就会立即消失: C/C++ 语 言 处 理括号的方法很 简单 ,将方括号前面的 值 和方括号内的 值 相加,得到一个新的指 针 ,再取指 针 所指的 对 象 值 。 “a[5]” 就完全等价于 “*(a+5)” , “5[a]” 就完全等价于 “*(5+a)” 。
那 么 “*(a+5)” 是什 么 运算呢?指 针 运算。因 为 在 编译 器 处 理 “int a[10];” 的 时 候,就等于定 义 了一个 “int * const a;” 同 时 将它初始化 为 指向 栈 内存的某 处 。
实际 上,正是因 为 “*(a+5)” 这种 用法 实 在太常用了, C 才 规 定了它的替代用法,后来 这 个替代用法被广 为 接受,而它的 实际 却被人 遗 忘。
以上内容本 书 未有提及, 这 是我看 书 看到 这 里的一点心得,作 为读书 笔 记 写下来。不是 为 了炫耀。本 书虽 然是 给 有一定基 础 的人 读 的,但是 毕 竟它只是按 步 就章地写下 C++ 的 语 法 规则 ,没有必要提及 这 些 技巧性高而又 实 用性少的内容。我之所以要写下来,目的是 为 了便于理解指 针 运算 。
标题:
:指
针
(二)
指
针
的初始化与
赋值
:指
针
是一个
变
量,它可以被
赋值
,也可以被求
值
。指
针
可以接受的
值
只有以下几
种
:
1 、 编译时 可求 值 的 0 值 常量。(必 须 是 0 ,其 实 就是 NULL 啦)
2 、 类 型匹配的 对 象的地址。(也就是用 & 运算符取一个 变 量的地址)
3 、另一 对 象末的下一地址。( 这种 用法主要用在循 环 里,其 实 当指 针 取 这 个 值时 , 对 其所指的内存 进 行存取往往会 导 致灾 难 )
4 、同 类 型的另一个有效指 针 (如 “p=q;” )。
其中第 1 点,将 0 值赋给 指 针 ,主要是 为 了有一个状 态 表示 这 个指 针 是 “ 空的 ” 。 C/C++ 通常 约 定 0 为 NULL , 虽 然的确存在地址 为 0 的内存,但是 别 指望用指 针 来 访问这 个内存。
初学者怎 样 才能消除 对 指 针 的恐惧?我 觉 得首要的一点是清醒地 认识 并且 时 刻提醒自己 “ 指 针 也是一个 变 量 ” 。比如以下两行程序:
int i;
int *p = &i;
看到 这 儿的人几乎无一例外把 p 和 i 联 系起来( 这 当然不是坏事),但是,我 觉 得更重要的是将 p 和 i 分离,心里 记 住, p 是一个 变 量, 该变 量是有它的 值 的, 这 个 值 与 i 的唯一 关 系是:目前 该值 正好等于 变 量 i 在内存中的位置。两 种 情况下 p 与 i 将毫无 关 系:
1 、 p 值 被改 变 ,如 “p = &j;” 或 “p++;”
2 、 i 变 量被 释 放,如离 开 了 i 的作用域。
1 、 编译时 可求 值 的 0 值 常量。(必 须 是 0 ,其 实 就是 NULL 啦)
2 、 类 型匹配的 对 象的地址。(也就是用 & 运算符取一个 变 量的地址)
3 、另一 对 象末的下一地址。( 这种 用法主要用在循 环 里,其 实 当指 针 取 这 个 值时 , 对 其所指的内存 进 行存取往往会 导 致灾 难 )
4 、同 类 型的另一个有效指 针 (如 “p=q;” )。
其中第 1 点,将 0 值赋给 指 针 ,主要是 为 了有一个状 态 表示 这 个指 针 是 “ 空的 ” 。 C/C++ 通常 约 定 0 为 NULL , 虽 然的确存在地址 为 0 的内存,但是 别 指望用指 针 来 访问这 个内存。
初学者怎 样 才能消除 对 指 针 的恐惧?我 觉 得首要的一点是清醒地 认识 并且 时 刻提醒自己 “ 指 针 也是一个 变 量 ” 。比如以下两行程序:
int i;
int *p = &i;
看到 这 儿的人几乎无一例外把 p 和 i 联 系起来( 这 当然不是坏事),但是,我 觉 得更重要的是将 p 和 i 分离,心里 记 住, p 是一个 变 量, 该变 量是有它的 值 的, 这 个 值 与 i 的唯一 关 系是:目前 该值 正好等于 变 量 i 在内存中的位置。两 种 情况下 p 与 i 将毫无 关 系:
1 、 p 值 被改 变 ,如 “p = &j;” 或 “p++;”
2 、 i 变 量被 释 放,如离 开 了 i 的作用域。
标题:
:指
针
指
针
是
C/C++
的精
华
,也是最
难
的部分。
——
所有学
习
C/C++
的人都明白
这
点,当年我初学的
时
候也是
这样
。但是,
现
在再回想指
针
,我却很
难
回
忆
它究竟
难
在哪儿。
应该说这
就叫
“
难
者不会,会者不
难
”
吧。
“
饱汉
不知
饿汉饥
”
是有一定的道理的,即使
饱汉
曾
经饿过
。
本 书 中 规 中矩地 讲 解了指 针 的概念、定 义 与初始化、操作等。正如上面提到的 “ 饱汉 不知 饿汉饥 ” ,我似乎很健忘,以至于不 记 得指 针 的 难 点在哪儿了。
指 针 的灵活性可以把大量的工作化繁 为 易,前提是必 须 首很把足 够 繁的指 针 弄懂。听起来有点像 绕 口令,事 实 就是 这样 ,你 现 在把 难 懂的 东 西弄懂了,日后可以把 难 事化 简 ,大事化小。
从 VB 过 来的人一定会熟悉 “ 值传递 ” 和 “ 地址 传递 ” 这 两个概念, 实际 上, “ 地址 传递 ” 这种说 法正是 为 了弥 补 VB 没有指 针 却有 类 似的需要才 发 明的。我 认为 C/C++ 程序 员 要想深入理解指 针 ,首先要抛弃 这 个概念。在 C/C++ 程序中,即使在函数 调 用中 传递 指 针 ,也不能 说 “ 地址 传递 ” , 还应该说 是 值传递 ,只不 过这 次 传递 的 值 有点特殊,特殊在于借用 这 个 值 ,可以找到其 它 值 。就好像我 给 你一把 钥 匙一 样 ,你通 过钥 匙可以 间 接 获 得更多,但是我 给 你的只不 过 是 钥 匙。
我前 阵 子曾写 过 一篇 关 于指 针 的文章,之所以写那篇文章,是因 为 看到一大堆初学者在 论坛 上提 问 。通 过对 他 们 提的 问题 的分析,我 总结 了几点。下面,首先就先引用我自己写的《 关 于指 针 》中的片段吧(完整的文章 请 到我的个人主 页查 找):
一、指 针 就是 变 量:
虽 然申明指 针 的 时 候也提 类 型,如:
char *p1;
int *p2;
float *p3;
double *p4;
.....
但是, 这 只表示 该 指 针 指 向某 类 型的数据,而不表示 该 指 针 的 类 型。 说 白了,指 针 都是一个 类 型:四字 节 无符号整数(将来的 64 位系 统 中可能有 变 化)。
二、指 针 的加减运算很特殊:
p++ 、 p-- 之 类 的运算并不是 让 p 这 个 “ 四字 节 无符号整数 ” 加一或减一,而是 让 它指向下一个或上一个存 储单 元,它 实际 加减的 值 就是它所指 类 型的 值 的 size 。
比如:
char * 型指 针 , 每 次加减的改 变 量都是 1 ;
float * 型的指 针 , 每 次加减的改 变 量都是 4 ;
void * 型指 针 无法加减。
还 要注意的是:指 针 不能相加,指 针 相减的差 为 int 型。
正是因 为 指 针 有着不同于其它 变 量的运算方式,所以,在任何 时 候用到指 针 都必 须 明确 “ 指 针 的 类 型 ” (即指 针 所指的 变 量的 类 型)。 这 就不 难 理解 为 什 么 函数声明 时 必 须 用 “int abc(char *p)” 而 调 用的 时 候却成了 “a = abc(p);” 这样 的形式了。
三、用指 针 做参数 传递 的是指 针值 ,不是指 针 本身:
要理解参数 传递 ,首先必 须 把 “ 形参 ” 与 “ 实 参 ” 弄明白。
函数 A 在 调 用函数 B 时 ,如果要 传递 一个参数 C , 实际 是在函数 B 中重新建立一个 变 量 C ,并将函数 A 中的 C 值传 入其中,于是函数 B 就可以使用 这 个 值 了,在函数 B 中,无 论 有没有修改 这 个 C 值 , 对 于函数 A 中的 C 都没有影响。函数 B 结 束 时 ,会将所有内存收回,局部 变 量 C 被 销毁 ,函数 B 对变 量 C 所做的一切修改都将被抛弃。
以上示例中,函数 A 中的 变 量 C 称 为 “ 实 参 ” ,函数 B 中的 变 量 C 被称 为 “ 形参 ” , 调 用函数 时 ,会在 B 函数体内建立一个形参, 该 形参的 值 与 实 参的 值 是相同的,但是形参的改 变 不影响 实 参,函数 结 束 时 ,形参被 销毁 , 实 参依然没有 发 生 变 化。
指 针 也是一个 变 量,所以它也符合以上的 规 定,但是,指 针 存放的不 仅仅 是一个 值 ,而是一个内存地址。 B 函数 对这 个地址 进 行了改 动 ,改 动 的并不是形参, 而是形参所指的内存。由于形参的 值 与 实 参的 值 完全相同,所以, 实 参所指的内存也被修改。函数 结 束 时 , 虽 然 这 个形参会被 销毁 ,指 针 的 变 化无法影响 实 参,但此前 对 它所指的内存的修改会持 续 有效。所以,把指 针 作 为 参数可以在被 调 函数( B )中改 变 主 调 函数( A )中的 变 量,好像形参影响了 实 参一 样 。
注意:是 “ 好像 ” 。在 这过 程中,函数 B 影响的不是参数,而是内存。
下面再来看 刚 才的例子: “int abc(char *p)” 和 “a = abc(p);” 。 为 什 么 申 请 中要用 * 号,因 为 函数必 须 知道 这 是指 针 ; 为 什 么调 用 时 不加 * 号,因 为传递 的是 “ 指 针值 ” ,而不是 “ 指 针 所指内存的 值 ” 。
四、指向指 针 的指 针 :
正因 为 指 针 也是一个 变 量,它一 样 要尊守形参与 实 参的 规 定。所以, 虽 然指 针 做参数可以将函数内 对变 量的修改 带 到函数外,但是,函数体内 对 指 针 本身作任何修都将被 丢 弃。如果要 让 指 针 本身被修改而且要影响函数外,那 么 ,被 调 函数就 应该 知道 “ 该 指 针 所在的内存地址 ” 。 这时 ,指 针 不再是指 针 ,而是 “ 普通 变 量 ” 。作 为 参数 传递 的不是 这 个 “ 普通 变 量 ” ,而是指向 这 个 “ 普通 变 量 ” 的指 针 。即 “ 指向指 针 的指 针 ” 。
如果 p 是一个指向指 针 的指 针 ,那 么 *p 就是一个指 针 ,我 们 不 妨就把它看成 q 。要 访问 q 指 针 所指的内存,只要 *q 就是了。用初中数学的 “ 等量代 换 ” 一 换 就知道, *q 就是 **p 。
五、指 针 数 组 。
之所以要把 “ 指 针 数 组 ” 单 独提出来,是因 为 数 组 本身就与指 针 有着千 丝 万 缕 的 关 系。即使你不想用指 针 ,只要你使用了数 组 , 实际 就在与指 针 打交道了。
只要理解了指 针 本身就是 变 量,就不 难 理解 “ 指 针 数 组 ” ,我 们 可以 暂 且把它当成普通数 组 来 处 理, a[0] 、 a[1] 、 a[2]…… 就是数 组 的元素,只是, a[0] 是一个指 针 , a[1] 、 a[2] 也是一个指 针 。那 a 呢?当然也是指 针 ,但 这 是两 码 事。你可以 完全无 视 a 的存在,只去管 a[0] 等元素。 *a[0] 与 *p 没有什 么 本 质 的区 别 。
还 有一个 东 西不得不提一下,它比 较 重要:
指 针 的定 义 有两个可取的方式,它 们 各有 优 缺点: “int *p;” 和 “int* p;” 是完全等价的,后者的好 处 是 让 人体会到 p 是一个 “ 指向 int 的 ” 指 针 ,前者会 让 人 误 解 为 *p 是一个 int 型 变 量( 这 里没有定 义 int 型 变 量);但是前者的好 处 是不会 产 生混淆,如 “int *p, *q;” 让 人一眼就看出定 义 了两个指 针 ,而 “int* p,q;” 会 让 人 误 解成定 义 了两个指 针 ( 实际 上 q 不是指 针 ) 。
本 书 中 规 中矩地 讲 解了指 针 的概念、定 义 与初始化、操作等。正如上面提到的 “ 饱汉 不知 饿汉饥 ” ,我似乎很健忘,以至于不 记 得指 针 的 难 点在哪儿了。
指 针 的灵活性可以把大量的工作化繁 为 易,前提是必 须 首很把足 够 繁的指 针 弄懂。听起来有点像 绕 口令,事 实 就是 这样 ,你 现 在把 难 懂的 东 西弄懂了,日后可以把 难 事化 简 ,大事化小。
从 VB 过 来的人一定会熟悉 “ 值传递 ” 和 “ 地址 传递 ” 这 两个概念, 实际 上, “ 地址 传递 ” 这种说 法正是 为 了弥 补 VB 没有指 针 却有 类 似的需要才 发 明的。我 认为 C/C++ 程序 员 要想深入理解指 针 ,首先要抛弃 这 个概念。在 C/C++ 程序中,即使在函数 调 用中 传递 指 针 ,也不能 说 “ 地址 传递 ” , 还应该说 是 值传递 ,只不 过这 次 传递 的 值 有点特殊,特殊在于借用 这 个 值 ,可以找到其 它 值 。就好像我 给 你一把 钥 匙一 样 ,你通 过钥 匙可以 间 接 获 得更多,但是我 给 你的只不 过 是 钥 匙。
我前 阵 子曾写 过 一篇 关 于指 针 的文章,之所以写那篇文章,是因 为 看到一大堆初学者在 论坛 上提 问 。通 过对 他 们 提的 问题 的分析,我 总结 了几点。下面,首先就先引用我自己写的《 关 于指 针 》中的片段吧(完整的文章 请 到我的个人主 页查 找):
一、指 针 就是 变 量:
虽 然申明指 针 的 时 候也提 类 型,如:
char *p1;
int *p2;
float *p3;
double *p4;
.....
但是, 这 只表示 该 指 针 指 向某 类 型的数据,而不表示 该 指 针 的 类 型。 说 白了,指 针 都是一个 类 型:四字 节 无符号整数(将来的 64 位系 统 中可能有 变 化)。
二、指 针 的加减运算很特殊:
p++ 、 p-- 之 类 的运算并不是 让 p 这 个 “ 四字 节 无符号整数 ” 加一或减一,而是 让 它指向下一个或上一个存 储单 元,它 实际 加减的 值 就是它所指 类 型的 值 的 size 。
比如:
char * 型指 针 , 每 次加减的改 变 量都是 1 ;
float * 型的指 针 , 每 次加减的改 变 量都是 4 ;
void * 型指 针 无法加减。
还 要注意的是:指 针 不能相加,指 针 相减的差 为 int 型。
正是因 为 指 针 有着不同于其它 变 量的运算方式,所以,在任何 时 候用到指 针 都必 须 明确 “ 指 针 的 类 型 ” (即指 针 所指的 变 量的 类 型)。 这 就不 难 理解 为 什 么 函数声明 时 必 须 用 “int abc(char *p)” 而 调 用的 时 候却成了 “a = abc(p);” 这样 的形式了。
三、用指 针 做参数 传递 的是指 针值 ,不是指 针 本身:
要理解参数 传递 ,首先必 须 把 “ 形参 ” 与 “ 实 参 ” 弄明白。
函数 A 在 调 用函数 B 时 ,如果要 传递 一个参数 C , 实际 是在函数 B 中重新建立一个 变 量 C ,并将函数 A 中的 C 值传 入其中,于是函数 B 就可以使用 这 个 值 了,在函数 B 中,无 论 有没有修改 这 个 C 值 , 对 于函数 A 中的 C 都没有影响。函数 B 结 束 时 ,会将所有内存收回,局部 变 量 C 被 销毁 ,函数 B 对变 量 C 所做的一切修改都将被抛弃。
以上示例中,函数 A 中的 变 量 C 称 为 “ 实 参 ” ,函数 B 中的 变 量 C 被称 为 “ 形参 ” , 调 用函数 时 ,会在 B 函数体内建立一个形参, 该 形参的 值 与 实 参的 值 是相同的,但是形参的改 变 不影响 实 参,函数 结 束 时 ,形参被 销毁 , 实 参依然没有 发 生 变 化。
指 针 也是一个 变 量,所以它也符合以上的 规 定,但是,指 针 存放的不 仅仅 是一个 值 ,而是一个内存地址。 B 函数 对这 个地址 进 行了改 动 ,改 动 的并不是形参, 而是形参所指的内存。由于形参的 值 与 实 参的 值 完全相同,所以, 实 参所指的内存也被修改。函数 结 束 时 , 虽 然 这 个形参会被 销毁 ,指 针 的 变 化无法影响 实 参,但此前 对 它所指的内存的修改会持 续 有效。所以,把指 针 作 为 参数可以在被 调 函数( B )中改 变 主 调 函数( A )中的 变 量,好像形参影响了 实 参一 样 。
注意:是 “ 好像 ” 。在 这过 程中,函数 B 影响的不是参数,而是内存。
下面再来看 刚 才的例子: “int abc(char *p)” 和 “a = abc(p);” 。 为 什 么 申 请 中要用 * 号,因 为 函数必 须 知道 这 是指 针 ; 为 什 么调 用 时 不加 * 号,因 为传递 的是 “ 指 针值 ” ,而不是 “ 指 针 所指内存的 值 ” 。
四、指向指 针 的指 针 :
正因 为 指 针 也是一个 变 量,它一 样 要尊守形参与 实 参的 规 定。所以, 虽 然指 针 做参数可以将函数内 对变 量的修改 带 到函数外,但是,函数体内 对 指 针 本身作任何修都将被 丢 弃。如果要 让 指 针 本身被修改而且要影响函数外,那 么 ,被 调 函数就 应该 知道 “ 该 指 针 所在的内存地址 ” 。 这时 ,指 针 不再是指 针 ,而是 “ 普通 变 量 ” 。作 为 参数 传递 的不是 这 个 “ 普通 变 量 ” ,而是指向 这 个 “ 普通 变 量 ” 的指 针 。即 “ 指向指 针 的指 针 ” 。
如果 p 是一个指向指 针 的指 针 ,那 么 *p 就是一个指 针 ,我 们 不 妨就把它看成 q 。要 访问 q 指 针 所指的内存,只要 *q 就是了。用初中数学的 “ 等量代 换 ” 一 换 就知道, *q 就是 **p 。
五、指 针 数 组 。
之所以要把 “ 指 针 数 组 ” 单 独提出来,是因 为 数 组 本身就与指 针 有着千 丝 万 缕 的 关 系。即使你不想用指 针 ,只要你使用了数 组 , 实际 就在与指 针 打交道了。
只要理解了指 针 本身就是 变 量,就不 难 理解 “ 指 针 数 组 ” ,我 们 可以 暂 且把它当成普通数 组 来 处 理, a[0] 、 a[1] 、 a[2]…… 就是数 组 的元素,只是, a[0] 是一个指 针 , a[1] 、 a[2] 也是一个指 针 。那 a 呢?当然也是指 针 ,但 这 是两 码 事。你可以 完全无 视 a 的存在,只去管 a[0] 等元素。 *a[0] 与 *p 没有什 么 本 质 的区 别 。
还 有一个 东 西不得不提一下,它比 较 重要:
指 针 的定 义 有两个可取的方式,它 们 各有 优 缺点: “int *p;” 和 “int* p;” 是完全等价的,后者的好 处 是 让 人体会到 p 是一个 “ 指向 int 的 ” 指 针 ,前者会 让 人 误 解 为 *p 是一个 int 型 变 量( 这 里没有定 义 int 型 变 量);但是前者的好 处 是不会 产 生混淆,如 “int *p, *q;” 让 人一眼就看出定 义 了两个指 针 ,而 “int* p,q;” 会 让 人 误 解成定 义 了两个指 针 ( 实际 上 q 不是指 针 ) 。
标题:
:数
组
进
入本
书
第四章,
开
始
讲
“
数
组
”
了。数
组难
不
难
?
这
不好
说
。但是数
组
非常重要
这
是肯定的,有
许
多基本的算法就是与数
组
一起出
现
的
——
比如冒泡排序法。而离
开
了那些算法,数
组
本身也失去了价
值
。
注意: 阅读 本章 时 要 对 “ 维 数 ” 概念加以小心,按平 时 的理解, “ 维 数 ” 是多 维 数 组 中的概念,但是本 书 中的 “ 维 数 ” 指的是元素个数。 为 了避免干 扰 ,我在 阅读 笔 记 中用 “ 个数 ” 来取代 “ 维 数 ” 。
引用:在出 现标 准 库 之前, C++ 程序大量使用数 组 保存一 组对 象 。而 现 代的 C++ 程序 则 更多地使用 vector 来取代数 组 ,数 组 被 严 格限制于程序内部使用,只有当性能 测试 表明使用 vector 无法达到必要的速度要求 时 ,才使用数 组 。
笔 记 :我汗一个先!原来我只是一个 “ 古代 ” 的 C++ 程序 员 。
数 组 的定 义 必需指明元素的个数,而且必 须 是 “ 常量表达式 ” 。 这 一点想必理解数 组 的人都会操作(即使弄 错 了, 编译 器也会立即 报错 ),但是真正去思考它的人也 许 不多。 这 里的 “ 常量 ” 概念是指程序在 编译阶 段就能求 值 的量。
在 C 时 代,我 们 并不会 过 多地理会 “ 常量 ” 这 个概念,那是因 为 C 时 代没有 const ,而 define 这 个 东 西, 谁 都知道它是在 编译 前就直接替 换 的。但是到了 C++ 时 代,由于 const 的存在,使 “ 常量 ” 这 个概念 变 得更普 杂 了。
const size1 = 5;// 这 是个 编译时 就可以求 值 的常量,它可以在数据的定 义 中使用。它的作用 仅仅 相当于 “define” 。
const size = getsize();// 这 个常量是运行 时 才能求 值 的。
除此之外, “ 常量求达式 ” 的概念 还 指明 这 可是以一个算 术 式,只要 编译时 能求 值 。如 “char username[max_name_size + 1];” 这种 用法非 常常用,因 为 它可以避免在定 义 数 组时 忘掉 NULL 。
数 组 成 员 的初始化可以 显 式指定,也可以不指定。 显 式指定 时 指定成 员 的个数可以小于等于 实际 个数,但不可以比 实际 个数大,如果小于, 则 其它元素 视为 未指定。 对 于未指定的元素的初始 值 ,它 们 遵循 变 量的初始化原 则 , 请 看《 变 量初始化》一文。
绝 大多数 书 本在介 绍 “ 字符串 ” 时 都会提到,字符串其 实 是字符数 组 ,只是因 为 它 们 太常用,才会有 专门 的使用方法。本 书 也不例外。
本 书 没有提数 组 的 实质 ,因 为 “ 指 针 ” 还 没有 进 入 读 者的眼睛。
引用:数 组 的 显 著缺点在于:数 组 的 长 度是固定的, 而且程序 员 无法知道一个 给 定数 组 的 长 度。
笔 记 :其 实这 正在程序 员 心中的一个痛。当我把数 组 作 为 参数 传 送 给 函数 时 ,函数 该 怎 么处 理 这 个 东 西?要 么 是 对 大小有个事先的 约 定 —— 这样 的程序将失去很多通用性,要 么连 大小一起 传递给 人家 。
注意: 阅读 本章 时 要 对 “ 维 数 ” 概念加以小心,按平 时 的理解, “ 维 数 ” 是多 维 数 组 中的概念,但是本 书 中的 “ 维 数 ” 指的是元素个数。 为 了避免干 扰 ,我在 阅读 笔 记 中用 “ 个数 ” 来取代 “ 维 数 ” 。
引用:在出 现标 准 库 之前, C++ 程序大量使用数 组 保存一 组对 象 。而 现 代的 C++ 程序 则 更多地使用 vector 来取代数 组 ,数 组 被 严 格限制于程序内部使用,只有当性能 测试 表明使用 vector 无法达到必要的速度要求 时 ,才使用数 组 。
笔 记 :我汗一个先!原来我只是一个 “ 古代 ” 的 C++ 程序 员 。
数 组 的定 义 必需指明元素的个数,而且必 须 是 “ 常量表达式 ” 。 这 一点想必理解数 组 的人都会操作(即使弄 错 了, 编译 器也会立即 报错 ),但是真正去思考它的人也 许 不多。 这 里的 “ 常量 ” 概念是指程序在 编译阶 段就能求 值 的量。
在 C 时 代,我 们 并不会 过 多地理会 “ 常量 ” 这 个概念,那是因 为 C 时 代没有 const ,而 define 这 个 东 西, 谁 都知道它是在 编译 前就直接替 换 的。但是到了 C++ 时 代,由于 const 的存在,使 “ 常量 ” 这 个概念 变 得更普 杂 了。
const size1 = 5;// 这 是个 编译时 就可以求 值 的常量,它可以在数据的定 义 中使用。它的作用 仅仅 相当于 “define” 。
const size = getsize();// 这 个常量是运行 时 才能求 值 的。
除此之外, “ 常量求达式 ” 的概念 还 指明 这 可是以一个算 术 式,只要 编译时 能求 值 。如 “char username[max_name_size + 1];” 这种 用法非 常常用,因 为 它可以避免在定 义 数 组时 忘掉 NULL 。
数 组 成 员 的初始化可以 显 式指定,也可以不指定。 显 式指定 时 指定成 员 的个数可以小于等于 实际 个数,但不可以比 实际 个数大,如果小于, 则 其它元素 视为 未指定。 对 于未指定的元素的初始 值 ,它 们 遵循 变 量的初始化原 则 , 请 看《 变 量初始化》一文。
绝 大多数 书 本在介 绍 “ 字符串 ” 时 都会提到,字符串其 实 是字符数 组 ,只是因 为 它 们 太常用,才会有 专门 的使用方法。本 书 也不例外。
本 书 没有提数 组 的 实质 ,因 为 “ 指 针 ” 还 没有 进 入 读 者的眼睛。
引用:数 组 的 显 著缺点在于:数 组 的 长 度是固定的, 而且程序 员 无法知道一个 给 定数 组 的 长 度。
笔 记 :其 实这 正在程序 员 心中的一个痛。当我把数 组 作 为 参数 传 送 给 函数 时 ,函数 该 怎 么处 理 这 个 东 西?要 么 是 对 大小有个事先的 约 定 —— 这样 的程序将失去很多通用性,要 么连 大小一起 传递给 人家 。
标题:
:
C++
标
准
库
,想
说爱
你不容
易
第三章就
这样结
束了,本章介
绍
了三个
标
准
库类
型:
string
、
vector
和
bitset
。
可惜的是,整个第三章我都是草草 读过 的。一方面因 为 它 们 不属于 严 格意 义 上的 C++ 内容,另一方面 C 时 代的 东 西在不 经 意 间 抵触着它 们 。
确切地 说 ,它 们 C 时 代的那些 东 西的替代品。它 们 存在的理由就是它 们 更 优 秀。然而 优 秀是一回事, 动 不 动 心又是一回事。
C 语 言在 类 与 对 象方面的缺失,使 C 程序 员 更多地掌握了底 层 的操作。面 对 C++ 标 准 库 中的 string 和 bitset 这 些 东 西, C 程序 员们 都知道,在 C++ 出来以前自己是怎 样 想方法 解决 过问题 的。刻苦才有刻骨 铭 心, 转 到 C++ 以后,是使用更 简单 安全的 标 准 库 , 还 是使用自己曾熟悉的老 办 法, 这 是一个取舍的 问题 。
引用:程序 员应优 先使用 标 准 库类类 型。
笔 记 :本 书 在 这 里放置了 这样 一个提示,大概是想告 诫 我 这样 的 顽 固派: “ 别 再死 纠 着 陈 腐旧套不放了,回 头 吧。 ” 其 实 ,早在本 书 的前言就有了 类 似的内容。可是,我依然不能 让 自己静下心来 细读 并熟 记 第三章。
也 许 有一天我会回 头 重 读 第三章,当然,也 许 永 远 不会。 因 为 像 string 和 vector 这样 的 东 西,在 MFC 中 还 有更好的替代品 。
可惜的是,整个第三章我都是草草 读过 的。一方面因 为 它 们 不属于 严 格意 义 上的 C++ 内容,另一方面 C 时 代的 东 西在不 经 意 间 抵触着它 们 。
确切地 说 ,它 们 C 时 代的那些 东 西的替代品。它 们 存在的理由就是它 们 更 优 秀。然而 优 秀是一回事, 动 不 动 心又是一回事。
C 语 言在 类 与 对 象方面的缺失,使 C 程序 员 更多地掌握了底 层 的操作。面 对 C++ 标 准 库 中的 string 和 bitset 这 些 东 西, C 程序 员们 都知道,在 C++ 出来以前自己是怎 样 想方法 解决 过问题 的。刻苦才有刻骨 铭 心, 转 到 C++ 以后,是使用更 简单 安全的 标 准 库 , 还 是使用自己曾熟悉的老 办 法, 这 是一个取舍的 问题 。
引用:程序 员应优 先使用 标 准 库类类 型。
笔 记 :本 书 在 这 里放置了 这样 一个提示,大概是想告 诫 我 这样 的 顽 固派: “ 别 再死 纠 着 陈 腐旧套不放了,回 头 吧。 ” 其 实 ,早在本 书 的前言就有了 类 似的内容。可是,我依然不能 让 自己静下心来 细读 并熟 记 第三章。
也 许 有一天我会回 头 重 读 第三章,当然,也 许 永 远 不会。 因 为 像 string 和 vector 这样 的 东 西,在 MFC 中 还 有更好的替代品 。
标题:
:
标
准
库
bitset
类
型
每
当使用到布
尔变
量数
组时
,
总
是有点心疼。因
为
布
尔变
量只需要
0
和
1
两
种值
,然而
编译
器
动辄
使用一个字
节
——
甚至四个字
节
来存放一个布
尔变
量。面
对
1/32
的使用效率,叫人怎能不心疼?
要想 节约 空 间 也不是没有 办 法,代价是写更多的代 码 :用 “&” 操作将 变 量的某一个 bit 取出来,一个字 节 就可以存放 8 个布 尔变 量。但是, 这 个代价是比 较 重的,重到足以 让 程序 员 望而生畏的地 步 。
bitset 应 运而生,它可以方便地管理一系列的 bit 位而不用程序 员 自己来写代 码 。
更重要的是, bitset 除了可以 访问 指定下 标 的 bit 位以外, 还 可以把它 们 作 为 一个整数来 进 行某些 统计 ,如:
b.any();//b 中是否存在置 为 1 的二 进 制位?
b.count();//b 中置 为 1 的二 进 制位的个数
……
不 过 , 话说 回来,我 还 是不 习惯 用 bitset ,原因在于我是从 C 语 言 转 到 C++ 的。 详细问题 留到后一篇文章中 讨论 。
要想 节约 空 间 也不是没有 办 法,代价是写更多的代 码 :用 “&” 操作将 变 量的某一个 bit 取出来,一个字 节 就可以存放 8 个布 尔变 量。但是, 这 个代价是比 较 重的,重到足以 让 程序 员 望而生畏的地 步 。
bitset 应 运而生,它可以方便地管理一系列的 bit 位而不用程序 员 自己来写代 码 。
更重要的是, bitset 除了可以 访问 指定下 标 的 bit 位以外, 还 可以把它 们 作 为 一个整数来 进 行某些 统计 ,如:
b.any();//b 中是否存在置 为 1 的二 进 制位?
b.count();//b 中置 为 1 的二 进 制位的个数
……
不 过 , 话说 回来,我 还 是不 习惯 用 bitset ,原因在于我是从 C 语 言 转 到 C++ 的。 详细问题 留到后一篇文章中 讨论 。
标题:
:迭代器:指
针
与数据
库杂
交的后代
迭代器与数据
库
的相似之
处
在于
end()
函数返回
值为
“
指向末元素的下一个
”
。跟数据
记录
集的
eof
这么
相似。
话说
回来,熟
练
于
C/C++
的程序
员
一定不会忘了,利用下
标访问
数
组时
用的
总
是用
“
半
开
半
闭
区
间
”
。就拿
“int a[10]; for(int i=0; i!=10; i++)”
来
说
,下
标为
10
就可以
视为
“
最后一个元素的下一个
”
。只是以前不会有
这么
明
显
的思考。
迭代器与指 针 的相似之 处 在于它的 “ 解引操作符 ” ,居然就是一个 “*” 号。而且存在着 “ivec[n]” 和 “*ivec” 这 两个完全等价的操作。而且 还 支 持算 术 运算来移 动 要 访问 的元素。
迭代的 const 用法与指 针 有个区 别 :
“vector<int>::const_iterator ivec = vec.begin();” 定 义 的 ivec 并不是常量,只是它指向的元素会得到保 护 。如果要定 义 本身 为 const 的迭代器,要用 “const vector<int>::ivec = vec.begin();” 。
指 针 是 这样处 理的:
const int *p = &i;//p 指向的 变 量是 const
int* const p = &i;//p 是 const
地雷:任何改 变 vector 长 度的操作都会使已存在的迭代器失 败 。例如,在 调 用 push_back 之后,就不能再信 赖 指向 vector 的迭代器的 值 了。
笔 记 :我不得不相信所 谓 的迭代器其本 质 就是一个指 针 了。因 为 vector 支持 动态 增 长 ,而且保 证 内存的 连续 。所以, 每 次改 变 它的 长 度都会 导 致 释 放已有内存、重新申 请 内存。 指 针 当然得失效 。
迭代器与指 针 的相似之 处 在于它的 “ 解引操作符 ” ,居然就是一个 “*” 号。而且存在着 “ivec[n]” 和 “*ivec” 这 两个完全等价的操作。而且 还 支 持算 术 运算来移 动 要 访问 的元素。
迭代的 const 用法与指 针 有个区 别 :
“vector<int>::const_iterator ivec = vec.begin();” 定 义 的 ivec 并不是常量,只是它指向的元素会得到保 护 。如果要定 义 本身 为 const 的迭代器,要用 “const vector<int>::ivec = vec.begin();” 。
指 针 是 这样处 理的:
const int *p = &i;//p 指向的 变 量是 const
int* const p = &i;//p 是 const
地雷:任何改 变 vector 长 度的操作都会使已存在的迭代器失 败 。例如,在 调 用 push_back 之后,就不能再信 赖 指向 vector 的迭代器的 值 了。
笔 记 :我不得不相信所 谓 的迭代器其本 质 就是一个指 针 了。因 为 vector 支持 动态 增 长 ,而且保 证 内存的 连续 。所以, 每 次改 变 它的 长 度都会 导 致 释 放已有内存、重新申 请 内存。 指 针 当然得失效 。
标题:
:
for
语
句的条件思
考
对
于
for
语
句,我几乎
总
是
这样
写的:
“for(int i=0; i<Max; i++);”
。但是,按本
书
的
说
法,我
这样
写似乎同
时
犯了两个
错误
。
1 、本 书 中写 for 语 句中的第二个表达式(条件表达式) 时总 是用 != ,而不用 < 。 这 几天来 虽 然心里 觉 得奇怪,但是一直没去思考 这 里面的含 义 。本 书 没有急着告 诉 我 “ 所以然 ” ,只是提醒我 读 完本 书 的第二部分后就会明白。我等着吧 :)
2 、我 总 是 觉 得用 Max 这样 一个 变 量去代替某个需要用函数才 能返回的 值 可以增加运行效率,但是本 书 的建 议 与我的理解相反。它建 议 用 “i<ivec.size()” , “ 因 为 数据 结 构可以 动态 增 长 , …… 如果确 实 增加了新元素的 话 ,那 么测试 已保存的 size 值 作 为 循 环结 束条件就会有 问题 ,因 为 没有将新加入的元素 计 算在内。 ”
同 时 ,本 书为 了打消运行效率的 顾虑 , 还 提到了 “ 内 联 函数 ” 这 个概念。 现 在 还 没有到介 绍 内 联 的 时 候,但是 C++ 必 须 是一个有机体,在 讲 述某一个知 识 点的 时 候,不可能完全避免另一个知 识 点。 所以,我 还 是建 议 初学者不要急着 阅读 本 书 。
1 、本 书 中写 for 语 句中的第二个表达式(条件表达式) 时总 是用 != ,而不用 < 。 这 几天来 虽 然心里 觉 得奇怪,但是一直没去思考 这 里面的含 义 。本 书 没有急着告 诉 我 “ 所以然 ” ,只是提醒我 读 完本 书 的第二部分后就会明白。我等着吧 :)
2 、我 总 是 觉 得用 Max 这样 一个 变 量去代替某个需要用函数才 能返回的 值 可以增加运行效率,但是本 书 的建 议 与我的理解相反。它建 议 用 “i<ivec.size()” , “ 因 为 数据 结 构可以 动态 增 长 , …… 如果确 实 增加了新元素的 话 ,那 么测试 已保存的 size 值 作 为 循 环结 束条件就会有 问题 ,因 为 没有将新加入的元素 计 算在内。 ”
同 时 ,本 书为 了打消运行效率的 顾虑 , 还 提到了 “ 内 联 函数 ” 这 个概念。 现 在 还 没有到介 绍 内 联 的 时 候,但是 C++ 必 须 是一个有机体,在 讲 述某一个知 识 点的 时 候,不可能完全避免另一个知 识 点。 所以,我 还 是建 议 初学者不要急着 阅读 本 书 。
标题:
:
标
准
库
vector
类
型
终
于
让
“
类
模板
”
上
场
了,可惜的是,在本
书
的第三章,
还
不能
彻
底
让类
模板浮出水面,只能将就着提一下。
引用:使用 类 模板可以 编 写一个 类 定 义 或函数定 义 ,而用于多个不同的数据 类 型。 ……vector 并不是一 种 数据 类 型,而只是一个 类 模板,可用来定 义 任意多 种 数据 类 型。
笔 记 : “vector<int> ivec;” 中, “vector<int>” 是一个数据 类 型, C++ 支持 类 模板,以后大量接触 类 模板 时 , 这 个一定要 时 刻注意。
引用:在元素 值 已知的情况下,最好是 动态 地增加元素。 …… 虽 然可以 对给 定元素个数的 vector 对 象 预 先分配内存,但是更有效的方法是先初始化一个空的 vector 对 象,然后再 动态 地增加元素。
将 vector 与 string 对 比,可以 轻 松地 记 住 empty() 、 size() 函数和 [] 、 = 、 == 、 != 等运算符。不 过这 似乎有个前提: 对 C 语 言比 较 熟并且 摈 弃 VB 的 String 变 量 类 型。因 为 , string 虽 然是一个 变 量,但是如果缺少 对 “char []” 的理解,将注定不能理解它。 —— 所以,我很反 对 某些人 说 的 “ 可以不学 C 语 言直 接学 C++” 论调 。
引用:使用 类 模板可以 编 写一个 类 定 义 或函数定 义 ,而用于多个不同的数据 类 型。 ……vector 并不是一 种 数据 类 型,而只是一个 类 模板,可用来定 义 任意多 种 数据 类 型。
笔 记 : “vector<int> ivec;” 中, “vector<int>” 是一个数据 类 型, C++ 支持 类 模板,以后大量接触 类 模板 时 , 这 个一定要 时 刻注意。
引用:在元素 值 已知的情况下,最好是 动态 地增加元素。 …… 虽 然可以 对给 定元素个数的 vector 对 象 预 先分配内存,但是更有效的方法是先初始化一个空的 vector 对 象,然后再 动态 地增加元素。
将 vector 与 string 对 比,可以 轻 松地 记 住 empty() 、 size() 函数和 [] 、 = 、 == 、 != 等运算符。不 过这 似乎有个前提: 对 C 语 言比 较 熟并且 摈 弃 VB 的 String 变 量 类 型。因 为 , string 虽 然是一个 变 量,但是如果缺少 对 “char []” 的理解,将注定不能理解它。 —— 所以,我很反 对 某些人 说 的 “ 可以不学 C 语 言直 接学 C++” 论调 。
标题:
:
标
准
库
string
类
型
习惯
了
VC++
的
CString
类
,而此前用的又是
C
语
言,所以,
压
根没有看一眼
string
。
现
在既然
书
中
专门讲
它,我就看一看吧。
定 义 与初始化:
string s1;
string s2(s1);
string s3("value");
string s4(n, 'c');// 由 n 个 c 组 成的一串
string 对 象的 输 入除了可以 “cin >> s1;” 以外, 还 可以将 cin 和 string 对 象一起作 为 getline() 函数的参数 “getline(cin, s1);” ,而且 这 个函数的返回 值还 是 cin 。
除此之外, string 类还 有 empty() 、 size() 等函数和 [] 、 + 、 = 、 == 等运算符。
地雷: empty() 函数的功能并不是将 对 象置空,而是 测试 是否 为 空。 这 可是与 CString 类 的 Empty() 函数不一 样 的哦!
本 书还详细 介 绍 了 empty::size_type 类 型存在的原因。在使用 VC++.NET 的 时 候,我已 经见过 了 size_t 类 型 ——strlen() 函数的返回 值 就是 size_t 类 型 对 象。当 时 我也没有 细 究 这 个 东 西,想来 应该 是 为 了支持 64 位机而 设 的吧。不 过现 在我知道了,原来我想得 还 不 够 。
引用:通 过这 些配套 类 型, 库类 型的使用就能与机器无 关 。
关 于 “+ 运算必 须 至少包含一个 string 类 型 ” , 这 可能 让 初学者摸不着 头脑 。比如 “s1 + s2” 、 “s1 + "hello"” 、 “"hello" + s1” 是合法的,唯独 “"hello" + "world"” 不合法。我在看懂 “ 运算符重 载 ” 之前也没有明白 这 个。本 书 由于 刚开头 ,离 “ 运算符重 载 ” 还远 着 呢,所以只告 诉读 者 “ 然 ” 、没有告 诉读 者 “ 所以然 ” 。我也不写了。
引用:下 标 操作可作左 值 。
笔 记 :人 类 一思考,上帝就 发 笑。 这 就是一个例 证 。下 标 操作可作左 值 有什 么 好 说 的?早就用 过 啊?比如 “char s[]="abcde"; s[2]='t';” 。可是 现 在的不是指 针变 量,而是 类 成 员 , 对 于 “string s("abcde");” 来 说 , “s[n]” 就不是 简单 操作一个内存了,而是从 “[] 重 载 函数中返回了一个 值 ” 。 这 个 值 可以做左 值 ,并不是想当然的事 。
定 义 与初始化:
string s1;
string s2(s1);
string s3("value");
string s4(n, 'c');// 由 n 个 c 组 成的一串
string 对 象的 输 入除了可以 “cin >> s1;” 以外, 还 可以将 cin 和 string 对 象一起作 为 getline() 函数的参数 “getline(cin, s1);” ,而且 这 个函数的返回 值还 是 cin 。
除此之外, string 类还 有 empty() 、 size() 等函数和 [] 、 + 、 = 、 == 等运算符。
地雷: empty() 函数的功能并不是将 对 象置空,而是 测试 是否 为 空。 这 可是与 CString 类 的 Empty() 函数不一 样 的哦!
本 书还详细 介 绍 了 empty::size_type 类 型存在的原因。在使用 VC++.NET 的 时 候,我已 经见过 了 size_t 类 型 ——strlen() 函数的返回 值 就是 size_t 类 型 对 象。当 时 我也没有 细 究 这 个 东 西,想来 应该 是 为 了支持 64 位机而 设 的吧。不 过现 在我知道了,原来我想得 还 不 够 。
引用:通 过这 些配套 类 型, 库类 型的使用就能与机器无 关 。
关 于 “+ 运算必 须 至少包含一个 string 类 型 ” , 这 可能 让 初学者摸不着 头脑 。比如 “s1 + s2” 、 “s1 + "hello"” 、 “"hello" + s1” 是合法的,唯独 “"hello" + "world"” 不合法。我在看懂 “ 运算符重 载 ” 之前也没有明白 这 个。本 书 由于 刚开头 ,离 “ 运算符重 载 ” 还远 着 呢,所以只告 诉读 者 “ 然 ” 、没有告 诉读 者 “ 所以然 ” 。我也不写了。
引用:下 标 操作可作左 值 。
笔 记 :人 类 一思考,上帝就 发 笑。 这 就是一个例 证 。下 标 操作可作左 值 有什 么 好 说 的?早就用 过 啊?比如 “char s[]="abcde"; s[2]='t';” 。可是 现 在的不是指 针变 量,而是 类 成 员 , 对 于 “string s("abcde");” 来 说 , “s[n]” 就不是 简单 操作一个内存了,而是从 “[] 重 载 函数中返回了一个 值 ” 。 这 个 值 可以做左 值 ,并不是想当然的事 。
标题:
:命名空
间
的
using
声明
虽
然
实际
工作中
绝
少用到
cin
和
cout
,但是我
还
是
记
得要用它
们
必先
“using namespace std;”
,至于
为
什
么这样
做,三个字:不知道。
本 书 从一 开头 就用到了 cin 和 cout ,但是它没有 using ,而是 每 一次用到它 们 都写成 “std::cin” 和 “std::cout” ,同 时 提醒 说这 两个字名是来自 std 命名空 间 的。于是,我似乎明白了 “using namespace std;” 的作用。
也 许 是 C++ 实 在太 难 , 作者 迟迟 不介 绍 更深入的内容,到了第一部分、第三章, 还 没有打算深入 using ,但又不得不 讲 一点,于是提了 这样 两行:
using std::cin;
using std::cout;
至此,究竟什 么 是命名空 间 ,命名空 间 是干什 么 的,我 还 是不懂。也 许书 后面会提吧 。
本 书 从一 开头 就用到了 cin 和 cout ,但是它没有 using ,而是 每 一次用到它 们 都写成 “std::cin” 和 “std::cout” ,同 时 提醒 说这 两个字名是来自 std 命名空 间 的。于是,我似乎明白了 “using namespace std;” 的作用。
也 许 是 C++ 实 在太 难 , 作者 迟迟 不介 绍 更深入的内容,到了第一部分、第三章, 还 没有打算深入 using ,但又不得不 讲 一点,于是提了 这样 两行:
using std::cin;
using std::cout;
至此,究竟什 么 是命名空 间 ,命名空 间 是干什 么 的,我 还 是不懂。也 许书 后面会提吧 。
标题:
:
头
文件
头
文件的用
处
主要是代
码
重用
——
重用不
仅仅
是
为
了减少工作量,
还
可以保
证每
一次重用都是完全相同的内容。
正因 为头 文件可以多次重用,所以要防止有些只能出 现 一次的代 码 放 进头 文件中。比如 变 量的定 义 只能有一次,声明(含 extern 且不含初始化)却可以有多次。函数也是。
引用:一些 const 对 象定 义 在 头 文件中。
笔 记 :看到 这 里, 总 算想通了前面的困惑: 为 什 么 const 常量的作用域 仅为 一个文件。正如我前面的估 计 , const 常量是不 开 劈内存空 间 的,代 码 在 编译 的 时 候就被直接替 换 成常量 值 。而且, 编译 器 对 多个 CPP 是分 开编译 的。所以,它必 须 要 编译 的 时 候知道 该 常量的 值 。如果常量的作用域也是全局的,那 么 我告 诉编译 器 “ 该 常量的 值 在另一个 CPP 中 ” 将使其无可适从。
避免 头 文件被重 复 包含是个比 较 有岐 义 的 说 法, 头 文件既然可以被多次包含, 为 什 么 又要避免重 复 包含? 还 是因 为 CPP 文件的 单 独 编译 。在 编译 任一个 CPP 文件 时 ,都要知道文件的内 容,所以,如果两个 CPP 都包含了同一个 头 文件,那 么头 文件就要被 编译 两次。但是同一个 CPP 如果重 复调 用 —— 甚至可能循 环调 用 —— 了某一个 头 文件, 则 要及 时 避免。 #ifndef...#define...#endif 可以起到 这 个作用。
对 了,我一直没想通 VC++.NET 的 “#pragma once” 是怎 么 工作的。它 为 什 么 不需要用 变 量来 标记 不同的 头 文件? 为 什 么 只需要一行也能 标记 出整个 头 文件的所有内容?
第一部分、第二章 结 束 。
正因 为头 文件可以多次重用,所以要防止有些只能出 现 一次的代 码 放 进头 文件中。比如 变 量的定 义 只能有一次,声明(含 extern 且不含初始化)却可以有多次。函数也是。
引用:一些 const 对 象定 义 在 头 文件中。
笔 记 :看到 这 里, 总 算想通了前面的困惑: 为 什 么 const 常量的作用域 仅为 一个文件。正如我前面的估 计 , const 常量是不 开 劈内存空 间 的,代 码 在 编译 的 时 候就被直接替 换 成常量 值 。而且, 编译 器 对 多个 CPP 是分 开编译 的。所以,它必 须 要 编译 的 时 候知道 该 常量的 值 。如果常量的作用域也是全局的,那 么 我告 诉编译 器 “ 该 常量的 值 在另一个 CPP 中 ” 将使其无可适从。
避免 头 文件被重 复 包含是个比 较 有岐 义 的 说 法, 头 文件既然可以被多次包含, 为 什 么 又要避免重 复 包含? 还 是因 为 CPP 文件的 单 独 编译 。在 编译 任一个 CPP 文件 时 ,都要知道文件的内 容,所以,如果两个 CPP 都包含了同一个 头 文件,那 么头 文件就要被 编译 两次。但是同一个 CPP 如果重 复调 用 —— 甚至可能循 环调 用 —— 了某一个 头 文件, 则 要及 时 避免。 #ifndef...#define...#endif 可以起到 这 个作用。
对 了,我一直没想通 VC++.NET 的 “#pragma once” 是怎 么 工作的。它 为 什 么 不需要用 变 量来 标记 不同的 头 文件? 为 什 么 只需要一行也能 标记 出整个 头 文件的所有内容?
第一部分、第二章 结 束 。
标题:
:
class
与
struct
首次考
虑
class
与
struct
的
关
系源自我
对类对
象占用内存数的
观
察。我
发现
VC++6
的
CString——
这么强
大的
类
——
它占内存居然是
4
字
节
(
sizeof(CString)
为
4
)。那
时
我就
认为
,
类对
象
仅仅
在内存中存放成
员变
量(《
C++ Primer
》称作
“
数据成
员
”
)而不存放成
员
函数。后来,
读
林
锐
博士的《高
质
量
C/C++
编
程指南》
时
,才看到了比
较
正
规
的
说
法:
C++
中
class
与
struct
没有本
质
的区
别
。
——
当
时
我将
这
句
话给
我朋友看
时
,他
还
表
现
出不相信的神色。
读 《 C++ Primer 》 时 ,我已 经 熟知了 class 与 struct 的 关 系。但是我 还 是 细细 地没有放 过书 中的任何一个字, 还 是作点 记录 吧:
引用:用 class 和 struct 关键 字定 义类 的唯一差 别 在于默 认访问级别 :默 认 情况下, struct 的成 员为 public ,而 class 的成 员为 private 。
笔 记 :不 过 ,一般情况下公司与公司 间规 定 协议时 ,喜 欢 将 类 定 义 写成 struct ,我想 这 可能是源于程序 员 从 C 语 言 继 承来的 习惯 , 还 有就是 协议 内容一般只包含数据,不包含函数,也 没有必要 规 定安全的 访问级别 。
引用: 编 程新手 经 常会忘 记类 定 义 后面的分号, 这 是个很普通的 错误 !
笔 记 : 书 中将 这 段文字 标 作一个地雷,我 还 是引用一下吧 。
读 《 C++ Primer 》 时 ,我已 经 熟知了 class 与 struct 的 关 系。但是我 还 是 细细 地没有放 过书 中的任何一个字, 还 是作点 记录 吧:
引用:用 class 和 struct 关键 字定 义类 的唯一差 别 在于默 认访问级别 :默 认 情况下, struct 的成 员为 public ,而 class 的成 员为 private 。
笔 记 :不 过 ,一般情况下公司与公司 间规 定 协议时 ,喜 欢 将 类 定 义 写成 struct ,我想 这 可能是源于程序 员 从 C 语 言 继 承来的 习惯 , 还 有就是 协议 内容一般只包含数据,不包含函数,也 没有必要 规 定安全的 访问级别 。
引用: 编 程新手 经 常会忘 记类 定 义 后面的分号, 这 是个很普通的 错误 !
笔 记 : 书 中将 这 段文字 标 作一个地雷,我 还 是引用一下吧 。
标题:
:枚
举
枚
举
是我向来不太喜
欢
用的
东
西,几乎我
见过
的
每
本
书
都是
这样
介
绍
枚
举
的:
enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
我看到 这 里, 总 是想:多浪 费 啊,与其 这样 , 还 不如直接用 0-6 这 些数呢。以后要写 “weekday Today = Sunday;” ,哪有 “int Today = 0;” 舒服。
本 书 似乎早看 透了我的心理,于是, 讲 枚 举 不从枚 举 入手,偏从 const 常量入手:
引用:
const int input = 0;
const int output = 1;
const int append = 2;
虽 然 这种 方法也能奏效,但是它有个明 显 的缺点:没有指出 这 些 值 是相 关联 的。 枚 举 提供了一 种 替代方法,不但定 义 了整数常量集,而且 还 把它 们 聚集成 组 。
笔 记 :大 师 就是大 师 ,他 们 写 书 能切中要害, 让读 者明白 标 准制定者的苦心。
另外,本 书 在 这 儿冷不丁地提了一个冷冰冰的概念:常量表达式。
引用:常量表达式是 编译 器在 编译时 就能 够计 算出 结 果的整型表达式。整型字面 值 常量是常表达式, ……
笔 记 : 让 我先汗一个。常量表达式必需是整数?我怎 么 不知道?那 “double pi = 3.14159;” 后面的字面 值 不属于常量表达式 吗 ? 这 是个疑 问 ,先 记 下来。
引用:枚 举类 型的 对 象的初始化或 赋值 ,只能通 过 其枚 举 成 员 或同一枚 举类 型的其它 对 象来 进 行。
笔 记 : 这 跟 现实 生活中的 “ 做人要 专 一 ” 有点相似,呵呵。 简单 地 说 ,你 选择 了用名字来代替数 值 ,那就得始 终 如一地使用名字,不可以用数 值 或其它表达式。 比如 “weekday Today = Sunday;” 不可写成 “weekday Today = 0;” , 虽 然 Sunday 就是 0 。
enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
我看到 这 里, 总 是想:多浪 费 啊,与其 这样 , 还 不如直接用 0-6 这 些数呢。以后要写 “weekday Today = Sunday;” ,哪有 “int Today = 0;” 舒服。
本 书 似乎早看 透了我的心理,于是, 讲 枚 举 不从枚 举 入手,偏从 const 常量入手:
引用:
const int input = 0;
const int output = 1;
const int append = 2;
虽 然 这种 方法也能奏效,但是它有个明 显 的缺点:没有指出 这 些 值 是相 关联 的。 枚 举 提供了一 种 替代方法,不但定 义 了整数常量集,而且 还 把它 们 聚集成 组 。
笔 记 :大 师 就是大 师 ,他 们 写 书 能切中要害, 让读 者明白 标 准制定者的苦心。
另外,本 书 在 这 儿冷不丁地提了一个冷冰冰的概念:常量表达式。
引用:常量表达式是 编译 器在 编译时 就能 够计 算出 结 果的整型表达式。整型字面 值 常量是常表达式, ……
笔 记 : 让 我先汗一个。常量表达式必需是整数?我怎 么 不知道?那 “double pi = 3.14159;” 后面的字面 值 不属于常量表达式 吗 ? 这 是个疑 问 ,先 记 下来。
引用:枚 举类 型的 对 象的初始化或 赋值 ,只能通 过 其枚 举 成 员 或同一枚 举类 型的其它 对 象来 进 行。
笔 记 : 这 跟 现实 生活中的 “ 做人要 专 一 ” 有点相似,呵呵。 简单 地 说 ,你 选择 了用名字来代替数 值 ,那就得始 终 如一地使用名字,不可以用数 值 或其它表达式。 比如 “weekday Today = Sunday;” 不可写成 “weekday Today = 0;” , 虽 然 Sunday 就是 0 。
标题:
:
typedef
长
期以来,我一直在疑惑:
typedef
这
个
词
要它干什
么
?因
为
没有它我照
样
可以完成所有任
务
。而有了它我反而
觉
得无法适
应
。比如
“UINT i;”
,
为
什
么
不写作
“unsigned int i;”
?本
书
用
简
短的三句
话
告
诉
我它存在的意
义
:
引用: typedef 通常被用于以下三 种 目的: 为 了 隐 藏特定 类 型的 实现 ,更 强调 使用 类 型的目的; 简 化 复杂 的 类 型定 义 ,使其更易理解;允 许 一 种类 型用于多个目的,同 时 使得 每 次使用 该类 型的目的明确。
笔 记 :第三 种 目的 对 我的启 发 特大。以后就 “typedef unsigned int age;” ,呵呵。
引用: typedef 通常被用于以下三 种 目的: 为 了 隐 藏特定 类 型的 实现 ,更 强调 使用 类 型的目的; 简 化 复杂 的 类 型定 义 ,使其更易理解;允 许 一 种类 型用于多个目的,同 时 使得 每 次使用 该类 型的目的明确。
笔 记 :第三 种 目的 对 我的启 发 特大。以后就 “typedef unsigned int age;” ,呵呵。
标题:
:引用
引用是
C++
的特色,一般用在函数的参数中。按有些
书
本的
说
法,叫
“
普通
变
量的用法,指
针变
量的效果
”
。
书
中本
节
没有
讲诉
引用在函数参数中的用法,只提了
“
给变
量起个
别
名
”
这
一个用
处
(
毕
竟本
书
才
开头
)。
说实
在的,如果撇
开
函数参数,
还
真想不到引用有什
么
用
处
。
引用 这 个概念本身也不 难 理解(除了 对 C 程序 员 来 说 有些不 习惯 以外),但是引用的符号却增加了理解它的 难 度,我 经 常在 论坛 上看到有初学者 对 “&” 和 “*” 两个符号的疑惑,他 们问 的 问题 可以 说 非常基 础 ,但却表 现 出了 这 个 问题 的 难 以理解的特点:
C 语 言中的指 针 已 经够复杂 的了,加再上一个引用,引用与指 针 有着千 丝 万 缕 的 联 系, 这 就算了,而且 还 用了 “&” 这 个符号。真 让 初学者忙昏了 头 。呵呵。下面四行程序,用到了两个 “&” 和两个 “*” ,但是它 们 的意 义 却全然不同:
int a;
int &b = a;//& 用在定 义 中 仅 表示 变 量的性 质为 引用
int *c = &a;//* 用在定 义 中 仅 表示 变 量的性 质为 指 针 , & 用在表达式中表示取地址
int d = *c;//* 用天表达式中表示取指 针变 量所指的 变 量的 值
写下以上文字,我 觉 得有些越 权 了。 这 些内容估 计 在本 书 后面会 详谈 的,我心急了点 。
引用 这 个概念本身也不 难 理解(除了 对 C 程序 员 来 说 有些不 习惯 以外),但是引用的符号却增加了理解它的 难 度,我 经 常在 论坛 上看到有初学者 对 “&” 和 “*” 两个符号的疑惑,他 们问 的 问题 可以 说 非常基 础 ,但却表 现 出了 这 个 问题 的 难 以理解的特点:
C 语 言中的指 针 已 经够复杂 的了,加再上一个引用,引用与指 针 有着千 丝 万 缕 的 联 系, 这 就算了,而且 还 用了 “&” 这 个符号。真 让 初学者忙昏了 头 。呵呵。下面四行程序,用到了两个 “&” 和两个 “*” ,但是它 们 的意 义 却全然不同:
int a;
int &b = a;//& 用在定 义 中 仅 表示 变 量的性 质为 引用
int *c = &a;//* 用在定 义 中 仅 表示 变 量的性 质为 指 针 , & 用在表达式中表示取地址
int d = *c;//* 用天表达式中表示取指 针变 量所指的 变 量的 值
写下以上文字,我 觉 得有些越 权 了。 这 些内容估 计 在本 书 后面会 详谈 的,我心急了点 。
标题:
:
const
常量的作用域
仅为
一个
CPP
文件
如果将
变
量定
义
放在任何
{}
的外面,
则该变
量是全局的,
这
个
规则
不适用于
const
常量。
以前 虽 然心里 隐隐约约 有 这 个感 觉 ,但是从未正面考 虑过这 个 问题 。之所以 隐隐约约 有此感 觉 ,是因 为 我 认为编译 器并不 为 const 常量 开 劈内存空 间 。
我曾 经专门 做 过测试 :程序如下:
const int i = 5;
int *p;
p = (int *)&i;
cout << *p << "/t" << i << endl;
(*p)++;
cout << *p << "/t" << i << endl;
测试结 果 发现 const 常量也是可以通 过 某些途径改 变 其 值 的,但是改 变 不起作用。我的解 释 是, 编 程器在生成机器 码时 将所有的 i 直接替 换 成了 5 。使得上面两个 cout 语 句都成了 “cout << *p << "/t" << 5 << endl;” 。
回想起以前做 过 的 测试 ,便不 难 理解 “const 常量的作用域 仅仅为 定 义 它的 CPP 文件 ” ,因 为编译 器是 对每 个 CPP 文件 单 独 编译 的,只有在 连 接 时 才会 去理会 CPP 之 间 的 关 系。 虽 然本 书 才看了个 开头 , 书 中 还说 “ 我 们 将会在 2.9.1 节 看到 为 何 const 对 象局部于文件 创 建 ” 。我不打算跳 跃 式地 阅读 本 书 ,所以,我 还 是先保留我自己的理解吧。
const 常量也 还 是可以成 为 全局常量的,方法是在定 义 的 时 候就加上 extern 。看到 这 里,我 终 于明白了《 extern 的困惑》中的困惑:原来,那 种 “ 有些多余,而且增加了出 错 的可能性 ” 的做法在常量的 处 理上派上了用 场 。
以前 虽 然心里 隐隐约约 有 这 个感 觉 ,但是从未正面考 虑过这 个 问题 。之所以 隐隐约约 有此感 觉 ,是因 为 我 认为编译 器并不 为 const 常量 开 劈内存空 间 。
我曾 经专门 做 过测试 :程序如下:
const int i = 5;
int *p;
p = (int *)&i;
cout << *p << "/t" << i << endl;
(*p)++;
cout << *p << "/t" << i << endl;
测试结 果 发现 const 常量也是可以通 过 某些途径改 变 其 值 的,但是改 变 不起作用。我的解 释 是, 编 程器在生成机器 码时 将所有的 i 直接替 换 成了 5 。使得上面两个 cout 语 句都成了 “cout << *p << "/t" << 5 << endl;” 。
回想起以前做 过 的 测试 ,便不 难 理解 “const 常量的作用域 仅仅为 定 义 它的 CPP 文件 ” ,因 为编译 器是 对每 个 CPP 文件 单 独 编译 的,只有在 连 接 时 才会 去理会 CPP 之 间 的 关 系。 虽 然本 书 才看了个 开头 , 书 中 还说 “ 我 们 将会在 2.9.1 节 看到 为 何 const 对 象局部于文件 创 建 ” 。我不打算跳 跃 式地 阅读 本 书 ,所以,我 还 是先保留我自己的理解吧。
const 常量也 还 是可以成 为 全局常量的,方法是在定 义 的 时 候就加上 extern 。看到 这 里,我 终 于明白了《 extern 的困惑》中的困惑:原来,那 种 “ 有些多余,而且增加了出 错 的可能性 ” 的做法在常量的 处 理上派上了用 场 。
标题:
:
变
量的作用域
作用域
这
个概念是程序
员
都耳熟能
详
的知
识
点。
这
部分知
识
我几乎可以跳
过
,不
过
我
还
是
认
真
阅读
了相
关
内容。
阅读过
程中
还
是有体会的:
有无数 书 本曾 经 提醒 过 我:少用(尽量不用)全局 变 量,多用局部 变 量。其 实 ,即使是局部 变 量,也 还 有作用域大小的,局部 变 量的作用域也是越小越好。原因自然和少用全局 变 量一个道理。正如 书 中所言: “ 通常把一个 对 象定 义 放在它首次使用的地方是一个很好的 办 法。 ”
以往,我使用局部 变 量 总 是把它放在函数的 开头 ,表示 这 些 变 量在本函数中起作用。 唯一的例外是 for 语 句的循 环变 量( for (int i=0; i<Max; i++); )。以后我就改改,把局部 变 量的作用域 缩 小到 仅仅 用到它的 语 句 块 。
有无数 书 本曾 经 提醒 过 我:少用(尽量不用)全局 变 量,多用局部 变 量。其 实 ,即使是局部 变 量,也 还 有作用域大小的,局部 变 量的作用域也是越小越好。原因自然和少用全局 变 量一个道理。正如 书 中所言: “ 通常把一个 对 象定 义 放在它首次使用的地方是一个很好的 办 法。 ”
以往,我使用局部 变 量 总 是把它放在函数的 开头 ,表示 这 些 变 量在本函数中起作用。 唯一的例外是 for 语 句的循 环变 量( for (int i=0; i<Max; i++); )。以后我就改改,把局部 变 量的作用域 缩 小到 仅仅 用到它的 语 句 块 。
标题:
:
extern
的困惑
extern
用来告
诉
程序:你不用
为
我的
变
量
开
劈内存空
间
,你只要知道
这
个
变
量
别处
已
经
声明
过
了。所以,我
总
是在程序包含多个
CPP
文件
时
才
这样
用:
1 、在某一个 CPP 文件中直接定 义变 量,如 int i = 0;
2 、其它 CPP 文件中声明 变 量,如 extern int i;
但是, 书 中介 绍 exturn 还 可以用来定 义 ,如: extern double pi = 3.1416; 特点是 该 extern 语 句包含 变 量的初始化。
我 觉 得 C++ 标 准 这样 做有些多余,而且增加了出 错 的可能性。因 为 “extern double pi = 3.1416;” 完全可以用 “double pi = 3.1416;” 来代替, 这样 做可以 让 定 义 与声明划清界 线 。 毕 竟定 义 只能有一次,而声明可以无数次。如果没有 这 个特性,可以 让 程序 员简单记为 “ 不 带 extern 的只能有一次, 带 extern 的可以有无数次,而且 extern 同 时 不能指定初始 值 。 ” 这 一特性的支持,使原本 简单 的 规则变 得 复杂 ,但没有 带 来灵活(众所周知 , C++ 的 复杂 是以高度灵活 为补尝 的)。
这 个段落 还让 我 认识 到了我以前使用 extern 的不足。我以往在同一个 CPP 文件中只使用一次 extern ,所以我往往是在文件 头 部用 extern 语 句来声明一下 变 量, 这样 做 虽 然没有什 么错 ,但却会 导 致上下翻 查 :有 时 在文件的某一 处 用到 变 量 时 ,想看一下它的声明,不得不把 滚动 条拖到 顶 上去 查 看。如果在用到它的段落 开头处 再声明,明 显 比 顶 部声明要好一些 。
1 、在某一个 CPP 文件中直接定 义变 量,如 int i = 0;
2 、其它 CPP 文件中声明 变 量,如 extern int i;
但是, 书 中介 绍 exturn 还 可以用来定 义 ,如: extern double pi = 3.1416; 特点是 该 extern 语 句包含 变 量的初始化。
我 觉 得 C++ 标 准 这样 做有些多余,而且增加了出 错 的可能性。因 为 “extern double pi = 3.1416;” 完全可以用 “double pi = 3.1416;” 来代替, 这样 做可以 让 定 义 与声明划清界 线 。 毕 竟定 义 只能有一次,而声明可以无数次。如果没有 这 个特性,可以 让 程序 员简单记为 “ 不 带 extern 的只能有一次, 带 extern 的可以有无数次,而且 extern 同 时 不能指定初始 值 。 ” 这 一特性的支持,使原本 简单 的 规则变 得 复杂 ,但没有 带 来灵活(众所周知 , C++ 的 复杂 是以高度灵活 为补尝 的)。
这 个段落 还让 我 认识 到了我以前使用 extern 的不足。我以往在同一个 CPP 文件中只使用一次 extern ,所以我往往是在文件 头 部用 extern 语 句来声明一下 变 量, 这样 做 虽 然没有什 么错 ,但却会 导 致上下翻 查 :有 时 在文件的某一 处 用到 变 量 时 ,想看一下它的声明,不得不把 滚动 条拖到 顶 上去 查 看。如果在用到它的段落 开头处 再声明,明 显 比 顶 部声明要好一些 。
标题:
:
变
量初始化
int ival(1024);//
直接初始化
int ival = 1024;// 复 制初始化
以前的我常用第二 种 用法,原因很 简单 :从来没 见过 第一 种 用法。直到后来学 习 了林 锐 博士的《高 质 量 C/C++ 编 程指南》。
那本 书 在 讲类 的构造 时说 道: CMyClass b = a; 这种 形式看起来像 赋值 , 实际 上 调 用的是拷 贝 构造函数。
那本 书给 我的感 觉仅仅 停留在 类变 量的初始化中,一直没有 过 渡到内置 变 量 类 型。直到后来,我在用 VC++.NET 的向 导 功能 编 程序 时 ,才 发现 向 导 帮我 产 生了 类 似于 “int i(100);” 这种语 法来初始化。
本 书 重点 强调 :初始化不是 赋值 ,
引用:当定 义 没有初始化的 变 量 时 ,系 统 有 时 候会帮我 们 初始化 变 量。 这时 ,系 统 提供什 么样 的 值 取决于 变 量的 类 型,也取决于 变 量定 义 的位置。 …… 内置 类 型 变 量是否初始化取决于 变 量定 义 的位置。在函数体外定 义 的 变 量都初始化 为 0 ,在函数体内定 义 的内置 类 型 变 量不 进 行自 动 初始化。 …… ( 类类 型)通 过 定 义 一个特殊的构造函数即默 认 构造函数来 实现 的。 这 个构造函数被称作默 认 构造函数,是因 为 它是 “ 默 认 ” 运行的。 …… 不管 变 量在哪里定 义 ,默 认 构造函数都会被使用。
笔 记 :林 锐 博士的《高 质 量 C/C++ 编 程指南》中 说 ,如果 类 没有定 义 无参数构造函数或拷 贝 构造函数,系 统 会自 动产 生 这 两个构造函数,它 们 采用最 简单 的 “ 值传递 ” 和 “ 位拷 贝 ” 来完成构造。
int ival = 1024;// 复 制初始化
以前的我常用第二 种 用法,原因很 简单 :从来没 见过 第一 种 用法。直到后来学 习 了林 锐 博士的《高 质 量 C/C++ 编 程指南》。
那本 书 在 讲类 的构造 时说 道: CMyClass b = a; 这种 形式看起来像 赋值 , 实际 上 调 用的是拷 贝 构造函数。
那本 书给 我的感 觉仅仅 停留在 类变 量的初始化中,一直没有 过 渡到内置 变 量 类 型。直到后来,我在用 VC++.NET 的向 导 功能 编 程序 时 ,才 发现 向 导 帮我 产 生了 类 似于 “int i(100);” 这种语 法来初始化。
本 书 重点 强调 :初始化不是 赋值 ,
引用:当定 义 没有初始化的 变 量 时 ,系 统 有 时 候会帮我 们 初始化 变 量。 这时 ,系 统 提供什 么样 的 值 取决于 变 量的 类 型,也取决于 变 量定 义 的位置。 …… 内置 类 型 变 量是否初始化取决于 变 量定 义 的位置。在函数体外定 义 的 变 量都初始化 为 0 ,在函数体内定 义 的内置 类 型 变 量不 进 行自 动 初始化。 …… ( 类类 型)通 过 定 义 一个特殊的构造函数即默 认 构造函数来 实现 的。 这 个构造函数被称作默 认 构造函数,是因 为 它是 “ 默 认 ” 运行的。 …… 不管 变 量在哪里定 义 ,默 认 构造函数都会被使用。
笔 记 :林 锐 博士的《高 质 量 C/C++ 编 程指南》中 说 ,如果 类 没有定 义 无参数构造函数或拷 贝 构造函数,系 统 会自 动产 生 这 两个构造函数,它 们 采用最 简单 的 “ 值传递 ” 和 “ 位拷 贝 ” 来完成构造。
标题:
:
变
量和
变
量名
引用:
对
象是内存中具有
类
型的区域。
笔 记 : 这 句 话说 得很直白,也只有 这样 面向 C++ 熟 练 工的 书 才可以 这样说 , 毕 竟初学者不知道内存与 变 量的 关 系,或者 还 没考 虑 到。
笔 记 : 这 句 话说 得很直白,也只有 这样 面向 C++ 熟 练 工的 书 才可以 这样说 , 毕 竟初学者不知道内存与 变 量的 关 系,或者 还 没考 虑 到。
引用:
C++
还
保留了一些
词
用作各操作符的替代名。
这
些替代名用于支持某些不支持
标
准
C++
操作符号集的字符集。它
们
也不能用作
标识
符(此
处
指
变
量名,
偷
猫
标
)。
一般的 书 只提到 C++ 变 量名不可以使用 关键 字,本 书还额 外提了 这样 一句,然后列出一个表,有 and 、 bitand 、 compl 、 not_eq 、 or_eq 、 xor_eq 、 and_eq 、 bitor 、 not 、 or 、 xor 。不 过 据我在 VC++.NET 上 测试 , 这 些是可以用作 变 量 标识 符的。
一般的 书 只提到 C++ 变 量名不可以使用 关键 字,本 书还额 外提了 这样 一句,然后列出一个表,有 and 、 bitand 、 compl 、 not_eq 、 or_eq 、 xor_eq 、 and_eq 、 bitor 、 not 、 or 、 xor 。不 过 据我在 VC++.NET 上 测试 , 这 些是可以用作 变 量 标识 符的。
都
说变
量名可以含
“_”
,
还
可以用
“_”
开头
,但我一直没有
试过
光光用一个
“_”
来做
变
量名,正好
习题
里有,我就
试
了一下,果然可以的
。
标题:
:
cout << (wchar_t
类
型
变
量
)
体
验
《
C++ Primer
》
说
了,字符常量或字符串常量前加
L
,表示
wchat_t
类
型,于是我
试
了一下:
程序如下:
char a = "a";
wchar_t b = L"a";
cout << a << endl;
cout << b << endl;
结 果如下:
a
0012FEBC
晕 ,怎 么 出 现这 个 结 果?
让 我再 试 。
char a = "a";
wchar_t b = L"a";
cout << a << endl;
cout << b << endl;
结 果如下:
a
0012FEBC
晕 ,怎 么 出 现这 个 结 果?
让 我再 试 。
程序如下:
char a = 'a';
wchar_t b = L'a';
cout << a << endl;
cout << b << endl;
结 果如下:
a
97
char a = 'a';
wchar_t b = L'a';
cout << a << endl;
cout << b << endl;
结 果如下:
a
97
由此可
见
,
cout
的
<<
运算符没有
对
wchat_t
的重
载
(或不健全)
。
标题:
:内置
类
型之精度
选择
引用:
……
大多数通用机器都是使用和
long
类
型一
样长
的
32
位来表示
int
类
型。整型运算
时
,用
32
位表示
int
类
型和用
64
位表示
long
类
型的机器会出
现应该选择
int
类
型
还
是
long
类
型的
难题
。在
这
些机器上,用
long
类
型
进
行
计
算所付出的运行
时
代价
远远
高于用
int
类
型
进
行同
样计
算的代价。
……
决定使用哪
种
浮点型就容易多了:使用
double
类
型基本上不会有
错
。在
float
类
型中
隐
式的精度
损
失是不能忽
视
的,而
double
类
型精度代价相
对
于
float
类
型精度代价可以忽略。
事
实
上,有些机器上,
double
类
型比
float
类
型的
计
算要快和多。
long double
类
型提供的精度通常没有必要,而且
还
需要承担
额
外的运行代价
。
标题:
:内置
类
型之
int
和
bool
第一部分,第二章
引用: C++ 标 准 规 定了 每 个算 术类 型的最小存 储 空 间 ,但它并不阻止 编译 器使用更大的存 储 空 间 ,事 实 上, 对 于 int 类 型,几乎所有的 编译 器使用的存 储 空 间 都比所要求的大。
笔 记 :确 实 如此, VC++ 中 int 和 long 是一 样 大。 VC++.NET 增加了 对 _int64 的支持。
引用:字符 类 型有两 种 : char 和 wchar_t , wchar_t 类 型用于 扩 展字符集,比如 汉 字和日 语 。
笔 记 :我怎 么 不知道 wchar_t ?我自己用包含 汉 字的字符串 时 用的也是 char 。 :(
看到 bool 型,我心里 对 VC++ 有些气 愤 。因 为 VC++ 里有一个 BOOL 宏。它的原型 为 “typedef int BOOL” 。既然 C++ 标 准已 经 有 bool 型, VC++ 加入 BOOL 的用意很明 显 :迎合更多的 编 程 习惯 。但是,即使非要增加 对 BOOL 的支持,我 认为 原型 应该这样 : “typedef bool BOOL” , 这样 更易于理解。
当然了, 长 期以来我一直没有注意到 bool 确 实 也不 应该 。 但是正是 BOOL 的存在,阻碍了我 对 bool 的理解。
引用: C++ 标 准 规 定了 每 个算 术类 型的最小存 储 空 间 ,但它并不阻止 编译 器使用更大的存 储 空 间 ,事 实 上, 对 于 int 类 型,几乎所有的 编译 器使用的存 储 空 间 都比所要求的大。
笔 记 :确 实 如此, VC++ 中 int 和 long 是一 样 大。 VC++.NET 增加了 对 _int64 的支持。
引用:字符 类 型有两 种 : char 和 wchar_t , wchar_t 类 型用于 扩 展字符集,比如 汉 字和日 语 。
笔 记 :我怎 么 不知道 wchar_t ?我自己用包含 汉 字的字符串 时 用的也是 char 。 :(
看到 bool 型,我心里 对 VC++ 有些气 愤 。因 为 VC++ 里有一个 BOOL 宏。它的原型 为 “typedef int BOOL” 。既然 C++ 标 准已 经 有 bool 型, VC++ 加入 BOOL 的用意很明 显 :迎合更多的 编 程 习惯 。但是,即使非要增加 对 BOOL 的支持,我 认为 原型 应该这样 : “typedef bool BOOL” , 这样 更易于理解。
当然了, 长 期以来我一直没有注意到 bool 确 实 也不 应该 。 但是正是 BOOL 的存在,阻碍了我 对 bool 的理解。
标题:
:第一章:快速入
门
第一章:快速入
门
本章的存在使本 书变 得不像一本 “ 规 范 书 ” ,似乎成了 “ 入 门书 ” , 这 可能是后来版本新加入的内容。以至于 这 一章 节 被排除在任何一个 “ 部分 ” 之外。
本章 “ 无厘 头 ” 地 简 要介 绍 了 cin 、 cout 、注 释 、 while 、 for 、 if 等概念。 这么 多 东 西, 每 一个都介 绍 点皮毛,然后 组 合成一个 综 合 实 例。
我称其 为 “ 无厘 头 ” 有以下原因:如果本 书 面 对 不了解 C++ 的 读 者,那 么这 一章似乎是有用的,但是 C++ 的入 门 者使用本 书显 然很 难 入 门 ,我不了解国外的情况怎 样 ,至少我身 边 的人是 这样 ;如果本 书 面 对 已 经 了解 C++ 的 读 者,那 么 , 这 些 东 西都不用介 绍 , 读 者也可以看懂那个 “ 综 合 实 例 ” ,而且所 谓 的 “ 综 合 实 例 ” 也没有存在的必要。
本章的存在使本 书变 得不像一本 “ 规 范 书 ” ,似乎成了 “ 入 门书 ” , 这 可能是后来版本新加入的内容。以至于 这 一章 节 被排除在任何一个 “ 部分 ” 之外。
本章 “ 无厘 头 ” 地 简 要介 绍 了 cin 、 cout 、注 释 、 while 、 for 、 if 等概念。 这么 多 东 西, 每 一个都介 绍 点皮毛,然后 组 合成一个 综 合 实 例。
我称其 为 “ 无厘 头 ” 有以下原因:如果本 书 面 对 不了解 C++ 的 读 者,那 么这 一章似乎是有用的,但是 C++ 的入 门 者使用本 书显 然很 难 入 门 ,我不了解国外的情况怎 样 ,至少我身 边 的人是 这样 ;如果本 书 面 对 已 经 了解 C++ 的 读 者,那 么 , 这 些 东 西都不用介 绍 , 读 者也可以看懂那个 “ 综 合 实 例 ” ,而且所 谓 的 “ 综 合 实 例 ” 也没有存在的必要。
不
过
有一个小小的收
获
:
int main()
{
return -1;
}
如果返回 值为 -1 , Windows 的 CMD 中运行也没有任何 额 外信息, 说 明 Windows 并不 报 告运行失 败 。
int main()
{
return -1;
}
如果返回 值为 -1 , Windows 的 CMD 中运行也没有任何 额 外信息, 说 明 Windows 并不 报 告运行失 败 。
标题:
:《〈
C++ Primer
〉
阅读
笔
记
》前
言
在
读
本
书
之前,我已
经
有
过
一段
编
写
C++
程序的
历
史,如果
连
C
语
言也算在内,可以追溯到十年前。用
BASIC
语
言
编
程序的
历
史
则
有十四年
(1992-2006)
。
长 期 编 程序中所使用的参考 书 无非有两 种 :介 绍 算法的 书 和介 绍语 法的 书 。我所 买 的参考 书 往往是同 时 介 绍 两者的。而 对语 法的介 绍 , 则 只是基于某一个 编译 器。
于是, 这 十年来,我所学 习 的 “C/C++” ,从本 质 上 说 只是 Turbo C 和 Visual C++ , 对 C/C++ 本身的理解也是被 编译 器 过滤 的内容。不是我不想去了解 C/C++ 的本 质 ,只是我 对 C/C++ 的 “ 法典 ” 有着与生 俱 来的恐惧。
这 个恐惧直到我 发现 了《 C++ Primer 中文版》,当我捧起 这 本 C++ 的 “ 圣 经 ” 时 ,我 终 于能理解 为 什 么每 有几十万人冒着被 踩 死的危 险 前去朝圣。
请 允 许 我用 “ 圣 经 ” 来比 喻这 本 书 ,我知道 这样 并不合适,因 为绝 大多数中国人并不知道 “ 圣 经 ” 的地位,但是我搜遍大 脑 的 每 一个角落也找不到其它合适的比 喻 。 这 源于中国人缺乏信仰。
在半年前,我曾 经带 着无限的敬仰 阅读 了林 锐 博士的《高 质 量 C/C++ 编 程指南》,并且及 时 培 养 / 修正了我的 编 程 习惯 。当然了,正如《 C++ Primer 》所言: “ 什 么 是 C 或 C++ 程序的正确格式存在着无休止的争 论 ……” 。所以,我所修正的只是 “ 自由体 ” ,而不是与林 锐 矛盾的方面。令我感到欣慰的是,《 C++ Primer 》居然也用一定的笔默来 讲 格式与 风 格,不同的是,它同 时 介 绍 几 种风 格,然后作出一个略 带倾 向性的建 议 。同 时 , 书 中 还 告 诫读 者: “ 一旦 选择 了某 种风 格,就要始 终 如一地使用。 ”
从 现 在起,我就要捧起 这 一本四五厘米厚、七百多 页 的圣 经 ,在 阅读过 程中, 难 免会有重点、要点要 记录 。我比 较爱书 ,不愿在 书 上做 标记 ,只好 选择 了 BLOG 这种 形式来做 读书 笔 记 。
谨 以此作 为 我的 BLOG 开 篇 说 明吧 。
长 期 编 程序中所使用的参考 书 无非有两 种 :介 绍 算法的 书 和介 绍语 法的 书 。我所 买 的参考 书 往往是同 时 介 绍 两者的。而 对语 法的介 绍 , 则 只是基于某一个 编译 器。
于是, 这 十年来,我所学 习 的 “C/C++” ,从本 质 上 说 只是 Turbo C 和 Visual C++ , 对 C/C++ 本身的理解也是被 编译 器 过滤 的内容。不是我不想去了解 C/C++ 的本 质 ,只是我 对 C/C++ 的 “ 法典 ” 有着与生 俱 来的恐惧。
这 个恐惧直到我 发现 了《 C++ Primer 中文版》,当我捧起 这 本 C++ 的 “ 圣 经 ” 时 ,我 终 于能理解 为 什 么每 有几十万人冒着被 踩 死的危 险 前去朝圣。
请 允 许 我用 “ 圣 经 ” 来比 喻这 本 书 ,我知道 这样 并不合适,因 为绝 大多数中国人并不知道 “ 圣 经 ” 的地位,但是我搜遍大 脑 的 每 一个角落也找不到其它合适的比 喻 。 这 源于中国人缺乏信仰。
在半年前,我曾 经带 着无限的敬仰 阅读 了林 锐 博士的《高 质 量 C/C++ 编 程指南》,并且及 时 培 养 / 修正了我的 编 程 习惯 。当然了,正如《 C++ Primer 》所言: “ 什 么 是 C 或 C++ 程序的正确格式存在着无休止的争 论 ……” 。所以,我所修正的只是 “ 自由体 ” ,而不是与林 锐 矛盾的方面。令我感到欣慰的是,《 C++ Primer 》居然也用一定的笔默来 讲 格式与 风 格,不同的是,它同 时 介 绍 几 种风 格,然后作出一个略 带倾 向性的建 议 。同 时 , 书 中 还 告 诫读 者: “ 一旦 选择 了某 种风 格,就要始 终 如一地使用。 ”
从 现 在起,我就要捧起 这 一本四五厘米厚、七百多 页 的圣 经 ,在 阅读过 程中, 难 免会有重点、要点要 记录 。我比 较爱书 ,不愿在 书 上做 标记 ,只好 选择 了 BLOG 这种 形式来做 读书 笔 记 。
谨 以此作 为 我的 BLOG 开 篇 说 明吧 。