《C++覆辙录》——第2章 语法问题2.1:数组定义和值初始化的语法形式混淆

第2章 语法问题

C++覆辙录
C++语言的语法和词法结构博大精深。此复杂性的一部分是从C语言那里继承而来的,另一部分则是为支撑某些特定的语言特性所要求的。

本章中我们将考察一组语法相关的头疼问题。其中有些属于常见的手误,但是错误的代码仍然能够通过编译,只不过会以出人意料的方式运行罢了。另外一些则是由于一段代码的语法结构及它们的运行期行为不再互为表里。其余的部分,我们主要研究语法层面的灵活余地带来的问题:明明是一字不差的代码,不同的软件工程师能从中得出大相径庭的结论来。

2.1:数组定义和值初始化的语法形式混淆

我们从堆上申请创建一个包含12个整数的数组,怎么样呀?没问题:
`
int *ip = new int(12); `
到目前为止似乎一切正常,那么让我们在数组上耍些花样。耍完以后,再把分配的内存予以回收。

for (int i = 0; i < 12; ++i)
   ip[i] = i;
delete [] ip;```
注意我们用的那对空的中括号,它告知编译器ip指涉到的是一个包含一堆整数的数组,而不是单个的一个整数。等等,事实真的是这样吗?

其实,ip指涉到的恰恰是单个的一个整数,被初始化成了12。我们犯了一个常见的手误,把一对中括号打成了一对小括号。这么一来,循环体里就会出现非法访问(索引值大于0的部分统统如此),内存回收这句也行不通了。可是,没有几个编译器能在编译期就把这个错误给逮出来。因为一个指针既可以指涉到单个的一个对象,也可以指涉到包含一堆对象的数组,而且循环体内的索引语句和数组的内存回收语句在语法意义上可谓无懈可击。这么一来,我们直到运行期才会意识到犯了错误。

也许连运行期都安然无恙。没错,访问对象数组所占空间结束之后的位置是非法的(虽然标准保证了访问对象数组结束之后的一个对象元素是可以的)1,把应用于数组的内存回收语法应用到并非数组的纯量上也是非法的。但做了非法的事,并不意味着你就没有机会逍遥法外(想想华尔街操盘手们干的勾当)。以上的代码在一些平台能够完美运行,但在另一些平台上则会在运行时崩溃,在某些特定平台上还会玩出别的古怪花样来,到底会如何,就全看特定的线程或进程在运行时是怎么操作堆内存了。正确的内存申请语法,当然如下所示:

int *ip = new int[12];`
说不定,最好的内存申请形式就是根本不去自己做这个内存申请:直接用标准库中的组件就好:

std::vector iv(12);
for (int i = 0; i < iv.size(); ++i)
   iv[i] = i;
// 不用显式地回收内存①```
①译者注:也不用显式地申请内存。
标准的`vector`模板几乎和手工写出的数组版本一样高效,但它更安全,编码更快,还起到了“自注释”的效果。一般地,相对于裸数组而言,优先使用`vector`模板。顺便提一句,相同的语法错误在一句平凡的声明语句里就会发生,但这个错误通常相对比较容易发现:

int a[12]; // 包含12个int型对象的数组
int b(12); // 一个int型对象,以12这个值来初始化之`

时间: 2024-09-18 23:36:51

《C++覆辙录》——第2章 语法问题2.1:数组定义和值初始化的语法形式混淆的相关文章

《C++覆辙录》——导读

前言 C++覆辙录 本书之渊薮乃是近20年的小小挫折.大错特错.不眠之夜和在键盘的敲击中不觉而过的无数周末.里面收集了普遍的.严重的或有意思的C++常见错误,共计九十有九.其中的大多数,(实在惭愧地说)都是我个人曾经犯过的. 术语"gotcha"1有其云谲波诡的形成历史和汗牛充栋的不同定义.但在本书中,我们将它定义为C++范畴里既普遍存在又能加以防范的编码和设计问题.这些常见错误涵盖了从无关大局的语法困扰,到基础层面上的设计瑕疵,再到源自内心的离经叛道等诸方面. 大约10年前,我开始在

《C++覆辙录》——常见错误1:过分积极的注释

第1章 基础问题 C++覆辙录 说一个问题是基础的,并不就是说它不是严重的或不是普遍存在的.事实上,本章所讨论的基础问题的共同特点比起在以后章节讨论的技术复杂度而言,可能更侧重于使人警醒.这里讨论的问题,由于它们的基础性,在某种程度上可以说它们普遍存在于几乎所有的C++代码中. 常见错误1:过分积极的注释 很多注释都是画蛇添足,它们只会让源代码更难读,更难维护,并经常把维护工程师引入歧途.考虑下面的简单语句: a = b; // 将b赋值给a 这个注释难道比代码本身更能说明这个语句的意义吗?因而

java se ee me语法相同,只是方法不同吗?还是连语法也不相同?

问题描述 java se ee me语法相同,只是方法不同吗?还是连语法也不相同? 1.java se ee me语法相同,只是方法不同吗?还是连语法也不相同? 2.java se是标准平台吗? 安卓SDK是方法? 3.可不可以不学SE,直接学java ee呢?主要想做网站,不想开发安卓程序? 解决方案 java是语言,se ee me是平台.语言只有一套,不存在什么语法差异.类库有三套,其中me是精简的,ee是企业的,前者规模要小. 学网站要学ee,se包含的基本类库ee中也用得着. 解决方案

《R语言编程艺术》——第3章 3.0 矩阵和数组

第3章 3.0 矩阵和数组 矩阵(matrix)是一种特殊的向量,包含两个附加的属性:行数和列数.所以矩阵也和向量一样,有模式的概念,例如数值型和字符型.(但反过来,向量却不能看作是只有一列或一行的矩阵.)数组(array)是R里更一般的对象,矩阵是数组的一个特殊情形.数组可以是多维的.例如一个三维的数组可以包含行.列和层(layer),而一个矩阵只有行和列两个维度.本章主要讨论矩阵,本章最后一节会简述更高维的数组.R的强大之处就在于它丰富的矩阵运算.本章主要讲述这些运算,尤其注重类似于向量的取

《C++覆辙录》——1.5:对引用的认识误区

1.55:对引用的认识误区 对于引用的使用,主要存在两个常见的问题.首先,它们经常和指针搞混.其次,它们未被充分利用.好多在C++工程里使用的指针实际上只是C阵营那些老顽固的杰作,该是引用翻身的时候了. 引用并非指针.引用只是其初始化物的别名.记好了,能对引用做的唯一操作就是初始化它.一旦初始化结束,引用就是其初始化物的另一种写法罢了(凡事皆有例外,请看常见错误44).引用是没有地址的,甚至它们有可能不占任何存储: int a = 12; int &ra = a; int *ip = &r

《C++覆辙录》——1.7:无视基础语言的精妙之处

1.7:无视基础语言的精妙之处 大多数C++软件工程师都自信满满地认为自己对所谓C++的"基础语言",也就是C++继承自C语言的那部分了如指掌.实际情况是,即使经验丰富的C++软件工程师,有时也会对最基础的C/C++语句和运算符的某些妙用一无所知. 逻辑运算符不能算难懂,对吗?但刚入行的C++软件工程师却总是不能让它们物尽其用.你看到下面的代码时是不是会怒从胆边生? bool r = false; if( a < b ) r = true;``` 正解如下: bool r = a

《C++覆辙录》——1.4:未能区分函数重载和形参默认值

1.4:未能区分函数重载和形参默认值 函数重载和形参默认值之间其实并无干系.不过,这两个独立的语言特征有时会被混淆,因为它们会模塑出语法上非常相像的函数用法接口.当然,看似一样的接口其背后的抽象意义却大相径庭: class C1 { public: void f1( int arg = 0 ); // ... };``` // ... C1 a; a.f1(0);a.f1();`型别C1的设计者决定给予函数f1()一个形参的默认值.这样一来,C1的使用者就有了两个选择:要么显式地给函数f1()一

《C++覆辙录》——2.11:运算符函数名字查找的反常行为

2.11:运算符函数名字查找的反常行为 重载的运算符真的只不过就是可以用中序语法调用的地地道道的成员函数或非成员函数罢了.它们是"语法糖": class String{ public: String &operator =(const String&); friend String operator +(const String&, const String&); String operator–(); operator const char*() co

《C++覆辙录》——2.3:(运算符)优先级问题

2.3:(运算符)优先级问题 本条款不讨论到底是伯爵夫人还是男爵夫人该在晚宴时坐在大使的旁座(此问题无解).我们要讨论的是在C++语言中的多层级化的运算符优先级如何带来一些令人困扰的问题. 优先级和结合性在一种程序设计语言中引入不同层级的运算符优先级通常来说是好事一桩,因为这样就可以不必使用多余的.分散注意力的括号而能把复杂表达式简化.(但是请注意,在复杂的或是比较晦涩的.亦即并非所有代码读者都能很好理解的表达式中显式地加上括号以表明意义,这是正确的想法.当然了,在那些平凡的.众人皆知的情况下一