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

简介:

本节书摘来自异步社区出版社《C++覆辙录》一书中的第2章,第2.1节,作者: 【美】Stephen C. Dewhurst(史蒂芬 C. 杜赫斯特),更多章节内容可以访问云栖社区“异步社区”公众号查看。

第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这个值来初始化之`

相关文章
|
25天前
|
存储 C++ 容器
学会在 C++ 中使用变量:从定义到实践
C++中的变量是数据容器,包括`int`、`double`、`char`、`string`和`bool`等类型。声明变量时指定类型和名称,如`int myNum = 15;`。`cout`与`&lt;&lt;`用于显示变量值。常量用`const`声明,值不可变。变量名应唯一,遵循特定命名规则,常量声明时需立即赋值。
112 1
|
25天前
|
存储 算法 编译器
【C++ 字符数组的模板特化】面向字符串的C++模板特化:理解与实践
【C++ 字符数组的模板特化】面向字符串的C++模板特化:理解与实践
47 1
|
29天前
|
编译器 C++
C++ 双冒号::开头的语法,::变量名,获取全局作用域变量
C++ 双冒号::开头的语法,::变量名,获取全局作用域变量
16 0
|
29天前
|
存储 编译器 Shell
【C++基础语法 枚举】解析 C/C++ 中枚举类型大小值
【C++基础语法 枚举】解析 C/C++ 中枚举类型大小值
14 0
|
24天前
|
自然语言处理 编译器 C语言
【C++ 20 新特性】参数包初始化捕获的魅力 (“pack init-capture“ in C++20: A Deep Dive)
【C++ 20 新特性】参数包初始化捕获的魅力 (“pack init-capture“ in C++20: A Deep Dive)
37 0
|
29天前
|
编译器 测试技术 C++
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
159 0
|
25天前
|
设计模式 算法 数据安全/隐私保护
【C++ 引用 】C++深度解析:引用成员变量的初始化及其在模板编程中的应用(二)
【C++ 引用 】C++深度解析:引用成员变量的初始化及其在模板编程中的应用
25 0
【C++ 引用 】C++深度解析:引用成员变量的初始化及其在模板编程中的应用(二)
|
25天前
|
存储 算法 编译器
【C++ 引用 】C++深度解析:引用成员变量的初始化及其在模板编程中的应用(一)
【C++ 引用 】C++深度解析:引用成员变量的初始化及其在模板编程中的应用
35 0
|
28天前
|
编译器 C++
深入理解 C++ 语法:从基础知识到高级应用
了解C++基础语法,包括`#include &lt;iostream&gt;`引入输入输出库,`using namespace std`简化命名。`int main()`是程序入口,`cout &lt;&lt; &quot;Hello World!&quot;`用于输出文本。换行可使用`\n`或`endl`。注释使用`//`进行单行注释,`/* */`进行多行注释。
26 0
|
2天前
|
编译器 C++
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
15 0