-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcpp-variables.html
1005 lines (1005 loc) · 184 KB
/
cpp-variables.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1"><meta name="renderer" content="webkit"><link rel="icon" type="image/ico" sizes="32x32" href="/assets/favicon.ico"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png"><link rel="alternate" href="/rss.xml" title="Jiankychen's Blog" type="application/rss+xml"><link rel="alternate" href="/atom.xml" title="Jiankychen's Blog" type="application/atom+xml"><link rel="alternate" type="application/json" title="Jiankychen's Blog" href="https://jiankychen.github.io/feed.json"><link rel="preconnect" href="https://lf9-cdn-tos.bytecdntp.com"><link rel="preconnect" href="https://at.alicdn.com"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Mulish:400,400italic,700,700italic%7CFredericka%20the%20Great:400,400italic,700,700italic%7CNoto%20Serif%20JP:400,400italic,700,700italic%7CNoto%20Serif%20SC:400,400italic,700,700italic%7CInconsolata:400,400italic,700,700italic&display=swap&subset=latin,latin-ext" media="none" onload="this.media='all'"><link rel="stylesheet" href="/css/app.css?v=0.4.2"><link rel="modulepreload" href="/js/chunk-7IVLRIQ3.js"><link rel="modulepreload" href="/js/chunk-IXT6LZJL.js"><link rel="modulepreload" href="/js/chunk-PHSEV26P.js"><link rel="modulepreload" href="/js/chunk-XHQGHZCW.js"><link rel="modulepreload" href="/js/comments-TUWNDU5I.js"><link rel="modulepreload" href="/js/post-P6IN2S3Y.js"><link rel="modulepreload" href="/js/quicklink-HAJEHOPK.js"><link rel="modulepreload" href="/js/search-WFXK2K66.js"><link rel="modulepreload" href="/js/siteInit.js"><link rel="stylesheet" href="https://npm.webcache.cn/@waline/[email protected]/dist/waline.css" media="none" onload="this.media='all'"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/e5221f7d85b0900837a45fb933fa34ec.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/99fb5ff897a82984470abf5e2a235d94.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://i.imgtg.com/2023/03/09/Y0hOs.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/8fe50780c15461b629c9aeab5a7f2acd.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/65d0bfef68566882ce0560cab2e87921.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/2aabaeb8aca379b991071d1c41632741.jpg" as="image" fetchpriority="high"><link rel="canonical" href="https://jiankychen.github.io/cpp-variables"><title>C++ 变量和基本类型</title><meta name="generator" content="Hexo 7.0.0"></head><body itemscope="" itemtype="http://schema.org/WebPage"><div id="loading"><div class="cat"><div class="body"></div><div class="head"><div class="face"></div></div><div class="foot"><div class="tummy-end"></div><div class="bottom"></div><div class="legs left"></div><div class="legs right"></div></div><div class="paw"><div class="hands left"></div><div class="hands right"></div></div></div></div><div id="container"><header id="header" itemscope="" itemtype="http://schema.org/WPHeader"><div class="inner"><div id="brand"><div class="pjax"><h1 itemprop="name headline">C++ 变量和基本类型</h1><div class="meta"><span class="item" title="创建时间:2022-01-01 17:29:09"><span class="icon"><i class="ic i-calendar"></i></span><span class="text">发表于</span><time itemprop="dateCreated datePublished" datetime="2022-01-01T17:29:09+08:00">2022-01-01</time></span><span class="item" title="本文字数"><span class="icon"><i class="ic i-pen"></i></span><span class="text">本文字数</span><span>37k</span><span class="text">字</span></span><span class="item" title="阅读时长"><span class="icon"><i class="ic i-clock"></i></span><span class="text">阅读时长</span><span>34 分钟</span></span></div></div></div><nav id="nav"><div class="inner"><div class="toggle"><div class="lines" aria-label="切换导航栏"><span class="line"></span><span class="line"></span><span class="line"></span></div></div><ul class="menu"><li class="item title"><a href="/" rel="start">Jiankychen</a></li></ul><ul class="right" id="rightNav"><li class="item theme"><i class="ic i-sun"></i></li><li class="item search"><i class="ic i-search"></i></li></ul></div></nav></div><div class="pjax" id="imgs"><ul><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/e5221f7d85b0900837a45fb933fa34ec.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/99fb5ff897a82984470abf5e2a235d94.jpg");"></li><li class="item" style="background-image: url("https://i.imgtg.com/2023/03/09/Y0hOs.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/8fe50780c15461b629c9aeab5a7f2acd.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/65d0bfef68566882ce0560cab2e87921.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/2aabaeb8aca379b991071d1c41632741.jpg");"></li></ul></div></header><div id="waves"><svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"><defs><path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"></path></defs><g class="parallax"><use xlink:href="#gentle-wave" x="48" y="0"></use><use xlink:href="#gentle-wave" x="48" y="3"></use><use xlink:href="#gentle-wave" x="48" y="5"></use><use xlink:href="#gentle-wave" x="48" y="7"></use></g></svg></div><main><div class="inner"><div class="pjax" id="main"><div class="article wrap"><div class="breadcrumb" itemlistelement="" itemscope="" itemtype="https://schema.org/BreadcrumbList"><i class="ic i-home"></i><span><a href="/">首页</a></span><i class="ic i-angle-right"></i><span class="current" itemprop="itemListElement" itemscope="itemscope" itemtype="https://schema.org/ListItem"><a href="/categories/C/" itemprop="item" rel="index" title="分类于C++"><span itemprop="name">C++<meta itemprop="position" content="0"></span></a></span></div><article class="post block" itemscope="itemscope" itemtype="http://schema.org/Article" lang="zh-CN"><link itemprop="mainEntityOfPage" href="https://jiankychen.github.io/cpp-variables.html"><span hidden="hidden" itemprop="author" itemscope="itemscope" itemtype="http://schema.org/Person"><meta itemprop="image" content="/assets/avatar.jpg"><meta itemprop="name" content="Jiankychen"><meta itemprop="description" content="Never put off till tomorrow what you can do today, "></span><span hidden="hidden" itemprop="publisher" itemscope="itemscope" itemtype="http://schema.org/Organization"><meta itemprop="name" content="Jiankychen's Blog"></span><div class="body md" itemprop="articleBody"><h1 id="基本内置类型"><a class="anchor" href="#基本内置类型">#</a> 基本内置类型</h1>
<p>C++ 定义了一套包括算术类型(arithmetic type)和空类型(void)在内的基本数据类型。其中,算术类型包含了字符、整数型、布尔值和浮点数。空类型不对应具体的值,仅用于一些特殊的场合,例如最常见的是,当函数不返回任何值时使用空类型作为返回类型。</p>
<h2 id="算术类型"><a class="anchor" href="#算术类型">#</a> 算术类型</h2>
<p>算术类型分为两类:整型(integral,包括字符和布尔类型在内)和浮点型。</p>
<table>
<thead>
<tr>
<th style="text-align:center">类型</th>
<th style="text-align:center">含义</th>
<th style="text-align:center">最小尺寸</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><code>bool</code></td>
<td style="text-align:center">布尔类型</td>
<td style="text-align:center">未定义</td>
</tr>
<tr>
<td style="text-align:center"><code>char</code></td>
<td style="text-align:center">字符</td>
<td style="text-align:center">8 位</td>
</tr>
<tr>
<td style="text-align:center"><code>wchar_t</code></td>
<td style="text-align:center">宽字符</td>
<td style="text-align:center">16 位</td>
</tr>
<tr>
<td style="text-align:center"><code>char16_t</code></td>
<td style="text-align:center">Unicode 字符</td>
<td style="text-align:center">16 位</td>
</tr>
<tr>
<td style="text-align:center"><code>char32_t</code></td>
<td style="text-align:center">Unicode 字符</td>
<td style="text-align:center">32 位</td>
</tr>
<tr>
<td style="text-align:center"><code>short</code></td>
<td style="text-align:center">短整型</td>
<td style="text-align:center">16 位</td>
</tr>
<tr>
<td style="text-align:center"><code>int</code></td>
<td style="text-align:center">整型</td>
<td style="text-align:center">16 位</td>
</tr>
<tr>
<td style="text-align:center"><code>long</code></td>
<td style="text-align:center">长整型</td>
<td style="text-align:center">32 位</td>
</tr>
<tr>
<td style="text-align:center"><code>long long</code></td>
<td style="text-align:center">长整型</td>
<td style="text-align:center">64 位</td>
</tr>
<tr>
<td style="text-align:center"><code>float</code></td>
<td style="text-align:center">单精度浮点数</td>
<td style="text-align:center">6 位有效数字</td>
</tr>
<tr>
<td style="text-align:center"><code>double</code></td>
<td style="text-align:center">双精度浮点数</td>
<td style="text-align:center">10 位有效数字</td>
</tr>
<tr>
<td style="text-align:center"><code>long double</code></td>
<td style="text-align:center">拓展精度浮点数</td>
<td style="text-align:center">10 位有效数字</td>
</tr>
</tbody>
</table>
<blockquote>
<p>上述表格列出了 C++ 标准规定的算术类型的尺寸的最小值,同时允许编译器赋予这些类型更大的尺寸</p>
</blockquote>
<h3 id="整型与浮点型"><a class="anchor" href="#整型与浮点型">#</a> 整型与浮点型</h3>
<p>布尔类型(bool)的取值是真(True)或者假(False)。</p>
<p>基本的字符类型是 <code>char</code> ,一个 <code>char</code> 的空间应确保可以存放机器基本字符集(如 ASCII 表)中任意字符对应的数字值,即,一个 <code>char</code> 的大小和一个机器字节一样。</p>
<p><code>wchar_t</code> 类型用于确保可以存放机器最大扩展字符集中的任意一个字符,类型 <code>char16_t</code> 和 <code>char32_t</code> 则为 Unicode 字符集服务(Unicode 是用于表示所有自然语言中字符的标准)。</p>
<blockquote>
<p>Unicode 为每种语言中的每个字符设定了统一并且唯一的二进制编码,现在用的是 UCS-2,即,2 个字节编码。UTF-8,UTF-16,UTF-32 均为字符编码方案。</p>
</blockquote>
<p>大多数机器的字节(byte)由 8 比特(bit)构成,字(word)则由 32 或 64 比特构成,也就是 4 或 8 字节。</p>
<ul>
<li>计算机以比特序列存储数据</li>
<li>字节是可寻址的最小内存块</li>
<li>字是内存的基本单元</li>
</ul>
<p>浮点型可表示单精度、双精度和扩展精度值。通常, <code>float</code> 以 1 个字(32 比特)来表示, <code>double</code> 以 2 个字(64 比特)来表示, <code>long double</code> 以 3 或 4 个字(96 或 128 比特)来表示。一般来说,类型 <code>float</code> 和 <code>double</code> 分别有 7 和 16 个有效位。</p>
<blockquote>
<p>浮点数在机器内用指数形式表示,分解为:数符,尾数,指数符,指数。</p>
</blockquote>
<h3 id="带符号类型和无符号类型"><a class="anchor" href="#带符号类型和无符号类型">#</a> 带符号类型和无符号类型</h3>
<p>除布尔型和扩展的字符型之外,其他整型可以划分为带符号的(signed)和无符号的(unsigned)两种。</p>
<ul>
<li>带符号类型可以表示正数、负数或 0</li>
<li>无符号类型仅能表示大于等于 0 的值。</li>
</ul>
<p>带符号类型: <code>int</code> 、 <code>short</code> 、 <code>long</code> 、 <code>long long</code></p>
<p>在这些类型名前添加 <code>unsigned</code> 就可以得到对应的无符号类型</p>
<p>即,无符号类型: <code>unsigned int</code> 、 <code>unsigned short</code> 、 <code>unsigned long</code> 、 <code>unsigned long long</code> 。</p>
<p>类型 <code>unsigned int</code> 可以缩写为 <code>unsigned</code> 。</p>
<p>字符型分为三种: <code>char</code> 、 <code>signed char</code> 、 <code>unsigned char</code> 。其中, <code>signed char</code> 类型和 <code>unsigned char</code> 类型分别为带符号类型和无符号类型, <code>char</code> 类型实际上会表现为上述两种形式中的一种,具体是哪种由编译器决定。</p>
<p>无符号类型中所有比特都用来存储值。</p>
<p>C++ 标准并没有规定带符号类型应如何表示,但是约定了在表示范围内正值和负值的量应该平衡。</p>
<blockquote>
<p><code>8</code> 比特的 <code>signed char</code> 理论上应该可以表示 <code>-127</code> 至 <code>127</code> 区间内的值,大多数现代计算机将 <code>8</code> 比特的 <code>signed char</code> 实际的表示范围定为 <code>-128~127</code> 。</p>
</blockquote>
<h3 id="如何选择类型"><a class="anchor" href="#如何选择类型">#</a> 如何选择类型</h3>
<p>当明确知晓数值不可能为负时,选用无符号类型。</p>
<p>使用 <code>int</code> 执行整数运算。若数值超出 <code>int</code> 的表示范围,选用 <code>long long</code> 。</p>
<p>在算术表达式中不要使用 <code>char</code> 或 <code>bool</code> ,只有在存放字符或布尔值时才使用它们。</p>
<blockquote>
<p>因为类型 <code>char</code> 在一些机器上是有符号的,而在另一些机器上又是无符号的,所以使用 <code>char</code> 进行运算特别容易出问题。如果需要使用一个不大的整数,则应该明确指定类型是 <code>signed char</code> 或者 <code>unsigned char</code> 。</p>
</blockquote>
<p>执行浮点数运算选用 <code>double</code> 。</p>
<blockquote>
<p>这是因为 <code>float</code> 通常精度不够而且双精度浮点数和单精度浮点数的计算代价相差无几。事实上,对于某些机器来说,双精度运算甚至比单精度还快。 <code>long double</code> 提供的精度在一般情况下是没有必要的,况且它带来的运行时消耗也不容忽视。</p>
</blockquote>
<h2 id="类型转换"><a class="anchor" href="#类型转换">#</a> 类型转换</h2>
<p>类型转换是指,将对象从一种给定的类型转换(convert)为另一种相关类型。</p>
<h3 id="转换规则简介"><a class="anchor" href="#转换规则简介">#</a> 转换规则(简介)</h3>
<p>类型所能表示的值的范围决定了转换的过程:</p>
<ol>
<li>
<p>当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为 <code>0</code> 则结果为 <code>false</code> ,否则结果为 <code>true</code> 。</p>
</li>
<li>
<p>当我们把一个布尔值赋给非布尔类型时,初始值为 <code>false</code> 则结果为 <code>0</code> ,初始值为 <code>true</code> 则结果为 <code>1</code> 。</p>
</li>
<li>
<p>当我们把一个浮点数赋给整数类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。</p>
</li>
<li>
<p>当我们把一个整数值赋给浮点类型时,小数部分记为 <code>0</code> 。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。</p>
</li>
<li>
<p>当我们给无符号类型赋一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如, <code>8</code> 比特大小的 <code>unsigned char</code> 可以表示 <code>0</code> 至 <code>255</code> 区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对 <code>256</code> 取模后所得的余数。因此,把 <code>-1</code> 赋给 <code>8</code> 比特大小的 <code>unsigned char</code> 所得的结果是 <code>255</code> 。</p>
</li>
<li>
<p>当我们给带符号类型赋一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。</p>
</li>
</ol>
<h3 id="含有无符号类型的表达式"><a class="anchor" href="#含有无符号类型的表达式">#</a> 含有无符号类型的表达式</h3>
<p>当一个算术表达式中既有无符号数又有 int 值时,那个 int 值就会转换成无符号数。</p>
<p><strong>如果表达式里既有带符号类型又有无符号类型</strong>,当带符号类型取值为负时会出现异常结果,这是因为<strong>带符号数会自动地转换成无符号数</strong></p>
<p>例如,在一个形如 <code>a * b</code> 的式子中,如果 <code>a = -1</code> , <code>b = 1</code> ,而且 <code>a</code> 和 <code>b</code> 都是 <code>int</code> ,则表达式的值显然为 <code>-1</code> 。然而,如果 <code>a</code> 是 <code>int</code> ,而 <code>b</code> 是 <code>unsigned</code> ,则结果须视在当前机器上 <code>int</code> 所占位数而定。在我们的环境里,结果是 <code>4294967295</code> 。</p>
<blockquote>
<p><strong>切勿混用带符号类型和无符号类型</strong></p>
</blockquote>
<p>当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值。</p>
<p>无符号数不会小于 0 这一事实同样关系到循环的写法。</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// 以降序的形式逐个输出数字 10 到 0</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span> i <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token operator">--</span>i<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> i <span class="token operator"><<</span>std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token comment">// 错误:变量 u 永远也不会小于 0,循环条件一直成立</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">unsigned</span> u <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">,</span> u <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token operator">--</span>u<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> u <span class="token operator"><<</span>std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>上例中,当 <code>u</code> 等于 <code>0</code> 时这次迭代输出 <code>0</code> ,然后继续执行 <code>for</code> 语句里的表达式。表达式 <code>--u</code> 从 <code>u</code> 当中减去 <code>1</code> ,得到的结果 <code>-1</code> 并不满足无符号数的要求,此时 <code>-1</code> 被自动地转换成一个合法的无符号数。假设 <code>int</code> 类型占 <code>32</code> 位,则当 <code>u</code> 等于 <code>0</code> 时, <code>--u</code> 的结果将会是 <code>4294967295</code> 。</p>
<h2 id="字面值常量"><a class="anchor" href="#字面值常量">#</a> 字面值常量</h2>
<p>字面值常量(literal):形如 42 的值,一望而知。</p>
<p>每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型。</p>
<h3 id="整型和浮点型字面值"><a class="anchor" href="#整型和浮点型字面值">#</a> 整型和浮点型字面值</h3>
<p>可以将整型字面值写作十进制数、八进制数或十六进制数的形式</p>
<ul>
<li>
<p>以 0 开头的整数代表八进制数</p>
</li>
<li>
<p>以 0x 或 0X 开头的代表十六进制数</p>
<pre><code> 20 //十进制
024 //八进制
0x14 //十六进制
</code></pre>
</li>
</ul>
<p>整型字面值具体的数据类型由它的值和符号决定。<strong>默认情况下,十进制字面值是带符号类型,八进制和十六进制字面值既可能是带符号的也可能是无符号的。</strong></p>
<ul>
<li>十进制字面值的类型是 <code>int</code> 、 <code>long</code> 和 <code>long long</code> 中尺寸最小的那个(例如,三者当中最小是 int),当然前提是这种类型要能容纳下当前的值</li>
<li>八进制和十六进制字面值的类型是能容纳其数值的 <code>int</code> 、 <code>unsigned int</code> 、 <code>long</code> 、 <code>unsigned long</code> 、 <code>long long</code> 和 <code>unsigned long long</code> (即,带符号的和无符号的 <code>int</code> 、 <code>long</code> 、 <code>long long</code> )中的尺寸最小者。如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误</li>
</ul>
<p>类型 <code>short</code> 没有对应的字面值</p>
<blockquote>
<p>尽管整型字面值可以存储在带符号数据类型中,但严格来说,十进制字面值不会是负数。如果我们使用了一个形如 - 42 的负十进制字面值,那个负号并不在字面值之内,它的作用仅仅是对字面值取负值而已。</p>
</blockquote>
<p>浮点型字面值表现为一个小数或以科学计数法表示的指数,其中指数部分用 E 或 e 标识:</p>
<pre><code>3.14159
3.14159E0
0.
0e0
.001
</code></pre>
<p>默认的,浮点型字面值是一个 <code>double</code> 。</p>
<h3 id="字符和字符串字面值"><a class="anchor" href="#字符和字符串字面值">#</a> 字符和字符串字面值</h3>
<p>由单引号括起来的一个字符称为字符型字面值</p>
<p>双引号括起来的零个或多个字符则构成字符串型字面值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token char">'a'</span> <span class="token comment">// 字符字面值</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token string">"Hello World!"</span> <span class="token comment">// 字符串字面值</span></pre></td></tr></tbody></table></figure><p>字符串字面值的类型实际上是由常量字符构成的数组(array)</p>
<p><strong>编译器在每个字符串字面值的结尾处添加一个空字符 ( <code>'\0'</code> )</strong> 。因此,字符串字面值的实际长度要比它的内容多 1。</p>
<p>例如,字面值 'A' 表示的就是单独的字符 A,而字符串 "A" 则代表了一个字符的数组,该数组包含两个字符:一个是字母 A、另一个是空字符。</p>
<p>如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体。</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// 当书写的字符串字面值比较长,写在一行里不太合适时,分多行书写字符串字面值</span></pre></td></tr><tr><td data-num="2"></td><td><pre>std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"a really, really long string literal "</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token operator"><<</span> <span class="token string">"that spans two lines"</span> <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><h3 id="转义序列"><a class="anchor" href="#转义序列">#</a> 转义序列</h3>
<p>有两类字符程序员不能直接使用:</p>
<ul>
<li>一类是不可打印(nonprintable)的字符,如退格或其他控制字符,因为它们没有可视的图符</li>
<li>另一类是在 C++ 语言中有特殊含义的字符(单引号、双引号、问号、反斜线)</li>
</ul>
<p>这些情况需要用到转义序列(escape sequence)</p>
<p>C++ 语言规定的转义序列包括:</p>
<blockquote>
<p>换行符 <code>\n</code> <br>
纵向制表符 <code>\v</code> <br>
反斜线 <code>\\</code> <br>
回车符 <code>\r</code> <br>
横向制表符 <code>\t</code> <br>
退格符 <code>\b</code> <br>
问号 <code>\?</code> <br>
进纸符 <code>\f</code> <br>
报警(响铃)符 <code>\a</code> <br>
双引号 <code>\"</code> <br>
单引号 <code>\'</code></p>
</blockquote>
<p>在程序中,上述转义序列被当作一个字符使用。</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span> <span class="token comment">// 转到新一行</span></pre></td></tr><tr><td data-num="2"></td><td><pre>std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"\tHi!\n"</span><span class="token punctuation">;</span> <span class="token comment">// 输出一个制表符,输出 "Hi!",转到新一行</span></pre></td></tr></tbody></table></figure><p>我们也可以使用泛化的转义序列,其形式是:</p>
<ul>
<li><strong> <code>\x</code> 后紧跟 1 个或多个十六进制数字</strong></li>
<li><strong> <code>\</code> 后紧跟 1 个、2 个或 3 个八进制数字</strong></li>
</ul>
<p>其中,数字部分表示的是字符对应的数值</p>
<blockquote>
<p>假设使用的是 Latin-1 字符集,以下是一些示例:(对应 ASCII 表)<br>
<code>\7</code> 响铃<br>
<code>\12</code> 换行符<br>
<code>\40</code> 空格<br>
<code>\0</code> 空字符<br>
<code>\115</code> 字符 M<br>
<code>\x4d</code> 字符 M</p>
</blockquote>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"Hi! \x4dO\115!\n"</span><span class="token punctuation">;</span> <span class="token comment">// 输出 "Hi MOM!",转到新一行</span></pre></td></tr><tr><td data-num="2"></td><td><pre>std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\115'</span> <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span> <span class="token comment">// 输出 "M",转到新一行</span></pre></td></tr></tbody></table></figure><p>注意:</p>
<ul>
<li>
<p>如果反斜线 <code>\</code> 后面跟着的八进制数字超过 3 个,只有前 3 个数字与 <code>\</code> 构成转义序列。例如, <code>"\1234"</code> 表示 2 个字符,即八进制数 123 对应的字符以及字符 4</p>
</li>
<li>
<p>而 <code>\x</code> 要用到后面跟着的所有数字,例如, <code>"\x1234"</code> 表示一个 16 位的字符,该字符由这 4 个十六进制数所对应的比特唯一确定。因为大多数机器的 <code>char</code> 型数据占 8 位,所以上面这个例子可能会报错。一般来说,超过 8 位的十六进制字符,都是与 下一节所示表格的某个前缀作为开头的扩展字符集 一起使用</p>
</li>
</ul>
<h3 id="指定字面值的类型"><a class="anchor" href="#指定字面值的类型">#</a> 指定字面值的类型</h3>
<p>通过添加前缀和后缀,可以改变整型、浮点型和字符型字面值的默认类型。</p>
<p>字符和字符串字面值:</p>
<table>
<thead>
<tr>
<th style="text-align:center">前缀</th>
<th style="text-align:center">含义</th>
<th style="text-align:center">类型</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">u</td>
<td style="text-align:center">Unicode 16 字符</td>
<td style="text-align:center">char16_t</td>
</tr>
<tr>
<td style="text-align:center">U</td>
<td style="text-align:center">Unicode 32 字符</td>
<td style="text-align:center">char32_t</td>
</tr>
<tr>
<td style="text-align:center">L</td>
<td style="text-align:center">宽字符</td>
<td style="text-align:center">wchar_t</td>
</tr>
<tr>
<td style="text-align:center">u8</td>
<td style="text-align:center">UTF-8 (仅用于字符串字面值常量)</td>
<td style="text-align:center">char</td>
</tr>
</tbody>
</table>
<p>整型字面值:</p>
<table>
<thead>
<tr>
<th style="text-align:center">后缀</th>
<th style="text-align:center">最小匹配类型</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">u 或 U</td>
<td style="text-align:center">unsigned</td>
</tr>
<tr>
<td style="text-align:center">l 或 L</td>
<td style="text-align:center">long</td>
</tr>
<tr>
<td style="text-align:center">ll 或 LL</td>
<td style="text-align:center">long long</td>
</tr>
</tbody>
</table>
<p>浮点型字面值:</p>
<table>
<thead>
<tr>
<th style="text-align:center">后缀</th>
<th style="text-align:center">最小匹配类型</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">f 或 F</td>
<td style="text-align:center">float</td>
</tr>
<tr>
<td style="text-align:center">l 或 L</td>
<td style="text-align:center">long double</td>
</tr>
</tbody>
</table>
<p>当使用一个长整型字面值时,请使用大写字母 L 来标记,因为小写字母 l 和数字 1 容易混淆</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>L<span class="token char">'a'</span> <span class="token comment">// 宽字符型字面值,类型是 wchar_t</span></pre></td></tr><tr><td data-num="2"></td><td><pre>u8<span class="token string">"hi!"</span> <span class="token comment">// UTF-8 字符串字面值,类型是 char</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token number">42ULL</span> <span class="token comment">// 无符号整型字面值,类型是 unsigned long long</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token number">1E-3F</span> <span class="token comment">// 单精度浮点型字面值,类型是 float</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token number">3.14159L</span> <span class="token comment">// 扩展精度浮点型字面值,类型是 long double</span></pre></td></tr></tbody></table></figure><p>对于一个整型字面值来说,我们能分别指定它是否带符号以及占用多少空间。</p>
<ul>
<li>如果后缀中有 <code>U</code> ,则该字面值属于无符号类型,也就是说,以 <code>U</code> 为后缀的十进制数、八进制数或十六进制数都将从 <code>unsigned int</code> 、 <code>unsigned long</code> 和 <code>unsigned long long</code> 中选择能匹配的空间最小的一个作为其数据类型</li>
<li>如果后缀中有 <code>L</code> ,则字面值的类型至少是 <code>long</code></li>
<li>如果后缀中有 <code>LL</code> ,则字面值的类型将是 <code>long long</code> 和 <code>unsigned long long</code> 中的一种</li>
<li>可以将 <code>U</code> 与 <code>L</code> 或 <code>LL</code> 合在一起使用。例如,以 <code>UL</code> 为后缀的字面值的数据类型将根据具体数值情况或者取 <code>unsigned long</code> ,或者取 <code>unsigned long long</code></li>
</ul>
<h3 id="布尔字面值和指针字面值"><a class="anchor" href="#布尔字面值和指针字面值">#</a> 布尔字面值和指针字面值</h3>
<p><code>true</code> 和 <code>false</code> 是布尔类型的字面值</p>
<p><code>nullptr</code> 是指针字面值</p>
<h1 id="变量"><a class="anchor" href="#变量">#</a> 变量</h1>
<p>C++ 中的每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围,以及变量能参与的运算</p>
<p>对 C++ 程序员来说,“变量(variable)” 和 “对象(object)” 一般可以互换使用。通常情况下,对象是指一块能存储数据并具有某种类型的内存空间</p>
<h2 id="变量定义"><a class="anchor" href="#变量定义">#</a> 变量定义</h2>
<p>变量定义的基本形式是:首先是类型说明符(type specifier),随后紧跟由一个或多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束</p>
<p>列表中每个变量名的类型都由类型说明符指定,定义时还可以为一个或多个变量赋初值,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> sum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> value<span class="token punctuation">,</span> <span class="token comment">// sum, value, and units_sold have type int</span></pre></td></tr><tr><td data-num="2"></td><td><pre> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// sum and units_sold have initial value 0</span></pre></td></tr><tr><td data-num="3"></td><td><pre>Sales_item item<span class="token punctuation">;</span> <span class="token comment">// item has type Sales_item (see § 1.5.1 (p. 20))</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token comment">// string is a library type, representing a variable-length sequence of characters</span></pre></td></tr><tr><td data-num="5"></td><td><pre>std<span class="token double-colon punctuation">::</span>string <span class="token function">book</span><span class="token punctuation">(</span><span class="token string">"0-201-78345-X"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// book initialized from string literal</span></pre></td></tr></tbody></table></figure><h3 id="初始化"><a class="anchor" href="#初始化">#</a> 初始化</h3>
<p>当对象在创建时获得了一个特定的值,我们说这个对象被初始化(initialized)了</p>
<p>在同一条定义语句中,可以用先定义的变量值去初始化后定义的其他变量,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// ok: price is defined and initialized before it is used to initialize discount</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">double</span> price <span class="token operator">=</span> <span class="token number">109.99</span><span class="token punctuation">,</span> discount <span class="token operator">=</span> price <span class="token operator">*</span> <span class="token number">0.16</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token comment">// ok: call applyDiscount and use the return value to initialize salePrice</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">double</span> salePrice <span class="token operator">=</span> <span class="token function">applyDiscount</span><span class="token punctuation">(</span>price<span class="token punctuation">,</span> discount<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p><strong>初始化不是赋值</strong></p>
<ul>
<li>初始化的含义是创建变量时赋予其一个初始值</li>
<li>赋值的含义是把对象的当前值擦除,而以一个新值来替代</li>
</ul>
<h4 id="列表初始化"><a class="anchor" href="#列表初始化">#</a> 列表初始化</h4>
<p>列表初始化(List Initialization): 用花括号来初始化变量,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> units_sold <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> units_sold<span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>当我们使用列表初始化以初始化内置类型的变量时,如果初始值存在丢失信息的风险,编译器将报错</p>
<p>例如,使用一个 <code>long double</code> 型的值来初始化一个 <code>int</code> 型的变量,此时可能丢失数据(至少 <code>ld</code> 的小数部分会丢失掉,而且 <code>int</code> 可能也存不下 <code>ld</code> 的整数部分),所以编译器拒绝 <code>a</code> 和 <code>b</code> 的初始化请求:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">long</span> <span class="token keyword">double</span> ld <span class="token operator">=</span> <span class="token number">3.1415926536</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> a<span class="token punctuation">{</span>ld<span class="token punctuation">}</span><span class="token punctuation">,</span> b <span class="token operator">=</span> <span class="token punctuation">{</span>ld<span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// error: narrowing conversion required</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token function">c</span><span class="token punctuation">(</span>ld<span class="token punctuation">)</span><span class="token punctuation">,</span> d <span class="token operator">=</span> ld<span class="token punctuation">;</span> <span class="token comment">// ok: but value will be truncated</span></pre></td></tr></tbody></table></figure><h4 id="默认初始化"><a class="anchor" href="#默认初始化">#</a> 默认初始化</h4>
<p>默认初始化(default initialized):如果定义变量时没有指定初值,则变量会被赋予 “默认值”</p>
<ol>
<li>
<p>如果是 <strong>内置类型的变量</strong> 未被显式初始化,它的值由定义的位置决定</p>
<ul>
<li><strong>定义于任何函数体之外的变量被初始化为 0</strong></li>
<li><strong>定义在函数体内部的内置类型变量将不被初始化(uninitialized)</strong></li>
</ul>
</li>
<li>
<p>每个类各自决定其初始化对象的方式,而且,是否可以不经初始化就定义对象也由类自己决定</p>
<ul>
<li>绝大多数类都支持无须显式初始化而定义对象,即,为对象提供了一个合适的默认值。例如, <code>string</code> 类规定:如果没有指定初值,则生成一个空串</li>
<li>一些类要求每个对象都显式初始化。此时,如果创建了一个该类的对象却并未对其做明确的初始化操作,将引发错误</li>
</ul>
</li>
</ol>
<blockquote>
<p>定义于函数体内的内置类型的对象如果没有初始化,则其值未定义<br>
类的对象如果没有显式地初始化,则其值由类确定</p>
</blockquote>
<p><strong>未初始化的变量含有一个不确定的值,使用未初始化的变量将带来无法预计的后果。</strong></p>
<blockquote>
<p>建议初始化每一个内置类型的变量</p>
</blockquote>
<h2 id="变量声明"><a class="anchor" href="#变量声明">#</a> 变量声明</h2>
<p>C++ 语言支持分离式编译(separatecompilation)机制,即,允许将程序分割为若干个文件,每个文件可被独立编译</p>
<p>特别地,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明</p>
<blockquote>
<ul>
<li>C++ 是一种静态类型(statically typed)语言,即,在编译阶段检查变量的类型。其中,检查类型的过程称为类型检查(type checking)</li>
<li>我们已经知道,对象的类型决定了对象所能参与的运算。在 C++ 语言中,编译器负责检查数据类型是否支持要执行的运算,如果试图执行类型不支持的运算,编译器将报错并且不会生成可执行文件</li>
<li>程序越复杂,静态类型检查越有助于发现问题。然而,前提是编译器必须知道每一个实体对象的类型,这就要求我们在使用某个变量之前必须声明其类型</li>
</ul>
</blockquote>
<p>变量声明(declaration):规定了变量的类型和名字</p>
<p>变量定义:不仅规定了变量的类型和名字,还申请存储空间,也可能会为变量赋一个初始值</p>
<p><strong>如果想声明一个变量,就在变量名前添加 <code>extern</code> 关键字</strong> ,注意,<strong>不要显式地初始化变量</strong> ,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">extern</span> <span class="token keyword">int</span> i<span class="token punctuation">;</span> <span class="token comment">// declares but does not define i</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> j<span class="token punctuation">;</span> <span class="token comment">// declares and defines j</span></pre></td></tr></tbody></table></figure><p><strong>任何包含显式初始化的声明即成为定义</strong> ,即,如果 <code>extern</code> 语句包含初始值,那么它就不再是声明,而是定义,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">extern</span> <span class="token keyword">double</span> pi <span class="token operator">=</span> <span class="token number">3.1416</span><span class="token punctuation">;</span> <span class="token comment">// definition</span></pre></td></tr></tbody></table></figure><p>在函数体内部,如果试图初始化一个由 <code>extern</code> 关键字标记的变量,将引发错误。即,包含有 <code>extern</code> 标记的定义,例如, <code>extern double pi=3.14;</code> ,不能放在函数体内部</p>
<p><strong>变量能且只能被定义一次,但是可以被多次声明</strong></p>
<blockquote>
<p>如果要在多个文件中使用同一个变量,必须将声明和定义分离:</p>
<ul>
<li>变量的定义必须出现在且只能出现在一个文件中</li>
<li>其他用到该变量的文件必须对其进行声明,但不能重复定义</li>
</ul>
</blockquote>
<h2 id="标识符"><a class="anchor" href="#标识符">#</a> 标识符</h2>
<p>C++ 的标识符(identifier)由 <strong>字母</strong> 、<strong>数字</strong> 和 <strong>下划线</strong> 组成,并且,<strong>必须以字母或下划线开头</strong></p>
<p>标识符的长度没有限制,但是,<strong>对大小写字母敏感</strong> ,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// defines four different int variables</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> somename<span class="token punctuation">,</span> someName<span class="token punctuation">,</span> SomeName<span class="token punctuation">,</span> SOMENAME<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p><strong>C++ 关键字 和 操作符替代名 不能被用作标识符</strong></p>
<p><img loading="lazy" data-src="Cpp2-%E5%8F%98%E9%87%8F%E5%92%8C%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B/1.webp" alt="关键字与操作符替代名"></p>
<p>此外,也还需要注意:</p>
<ul>
<li>标识符不能连续出现两个下划线</li>
<li>不能以下划线紧连大写字母开头</li>
<li>定义在函数体外的标识符不能以下划线开头</li>
</ul>
<p>变量命名规范变量命名有许多约定俗成的规范,以有效提高程序的可读性:</p>
<ul>
<li>标识符要能体现实际含义</li>
<li>变量名一般用小写字母,如 <code>index</code> ,而不使用 <code>Index</code> 或 <code>INDEX</code></li>
<li>用户自定义的类名一般以大写字母开头,如 <code>Sales_item</code></li>
<li>如果标识符由多个单词组成,则单词间应有明显区分,如下划线命名法 <code>student_loan</code> 或驼峰命名法 <code>studentLoan</code> ,而不要使用 <code>studentloan</code></li>
</ul>
<h2 id="名字的作用域"><a class="anchor" href="#名字的作用域">#</a> 名字的作用域</h2>
<p>不论是在程序的什么位置,使用到的每个名字都会指向一个特定的实体:变量、函数、类型等。然而,同一个名字如果出现在程序的不同位置,也可能指向的是不同实体</p>
<p>作用域(scope),可简单理解为 名字的有效区域</p>
<ul>
<li>
<p>C++ 语言中大多数作用域都以花括号分隔</p>
</li>
<li>
<p>同一个名字在不同的作用域中可能指向不同的实体</p>
</li>
<li>
<p>名字的有效区域始于名字的声明语句,止于声明语句所在的作用域末端</p>
</li>
</ul>
<blockquote>
<p>A scope is a part of the program in which a name has a particular meaning.</p>
</blockquote>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token keyword">int</span> sum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token comment">// sum values from 1 through 10 inclusive</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> val <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> val <span class="token operator"><=</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token operator">++</span>val<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre> sum <span class="token operator">+=</span> val<span class="token punctuation">;</span> <span class="token comment">// equivalent to sum = sum + val</span></pre></td></tr><tr><td data-num="7"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"Sum of 1 to 10 inclusive is "</span></pre></td></tr><tr><td data-num="8"></td><td><pre> <span class="token operator"><<</span> sum <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>上例中, <code>main</code> 定义于所有花括号之外,具有 <strong>全局作用域</strong>(global scope),在整个程序的范围内都可使用; <code>sum</code> 定义于 <code>main</code> 函数所限定的作用域内,在 <code>main</code> 函数作用域内任意位置都可以访问,但无法在 <code>main</code> 函数之外访问,即,具有 <strong>块作用域</strong>(block scope)</p>
<blockquote>
<p>一般来说,在对象第一次被使用的地方附近定义它是一种好的选择</p>
</blockquote>
<p>作用域可以彼此包含,被包含(或者说被嵌套)的作用域称为 <strong>内层作用域</strong>(inner scope),如上例中 <code>val</code> 的作用域;包含着别的作用域的作用域称为 <strong>外层作用域</strong>(outer scope),如上例中 <code>sum</code> 的作用域</p>
<p>作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字。但与此同时,允许在内层作用域中重新定义外层作用域已有的名字:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token comment">// Program for illustration purposes only: It is bad style for a function</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token comment">// to use a global variable and also define a local variable with the same name</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">int</span> reused <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">// reused has global scope</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">{</span></pre></td></tr><tr><td data-num="7"></td><td><pre> <span class="token keyword">int</span> unique <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// unique has block scope</span></pre></td></tr><tr><td data-num="8"></td><td><pre> <span class="token comment">// output #1: uses global reused; prints 42 0</span></pre></td></tr><tr><td data-num="9"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> reused <span class="token operator"><<</span> <span class="token string">" "</span> <span class="token operator"><<</span> unique <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre> <span class="token keyword">int</span> reused <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// new, local object named reused hides global reused</span></pre></td></tr><tr><td data-num="11"></td><td><pre> <span class="token comment">// output #2: uses local reused; prints 0 0</span></pre></td></tr><tr><td data-num="12"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> reused <span class="token operator"><<</span> <span class="token string">" "</span> <span class="token operator"><<</span> unique <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre> <span class="token comment">// output #3: explicitly requests the global reused; prints 42 0</span></pre></td></tr><tr><td data-num="14"></td><td><pre> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token double-colon punctuation">::</span>reused <span class="token operator"><<</span> <span class="token string">" "</span> <span class="token operator"><<</span> unique <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p><strong>如果函数有可能用到某个全局变量,则不宜再定义一个同名的局部变量</strong></p>
<h1 id="复合类型"><a class="anchor" href="#复合类型">#</a> 复合类型</h1>
<p>复合类型(compound type)是指基于基本数据类型定义的类型</p>
<p>这里将介绍两种复合类型:<strong>引用</strong> 和 <strong>指针</strong></p>
<h2 id="引用"><a class="anchor" href="#引用">#</a> 引用</h2>
<blockquote>
<p>这里的 “引用(reference)” ,指的其实是 “左值引用(lvalue reference)”</p>
</blockquote>
<p>引用(reference)为对象起了另外一个名字,即,别名</p>
<p>通过将声明符写成 <code>&d</code> 的形式来定义引用类型,其中 <code>d</code> 是声明的变量名</p>
<p>如下所示, <code>refVal</code> 是 <code>ival</code> 的一个引用</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> ival <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>refVal <span class="token operator">=</span> ival<span class="token punctuation">;</span> <span class="token comment">//refVal 指向 ival(是 ival 的另一个名字)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>refVal2<span class="token punctuation">;</span> <span class="token comment">// 报错:引用必须被初始化</span></pre></td></tr></tbody></table></figure><p>定义引用时,程序把 引用 和 它的初始值 绑定(bind)在一起,而不是将初始值拷贝给引用</p>
<ul>
<li>一旦初始化完成,<strong>引用 将和 它的初始值对象 一直绑定在一起</strong></li>
<li>注意,无法将引用重新绑定到另外一个对象,因此,<strong>引用必须初始化</strong></li>
</ul>
<h3 id="引用即别名"><a class="anchor" href="#引用即别名">#</a> 引用即别名</h3>
<p>引用并不是对象,它只是为一个已经存在的对象额外起一个名字</p>
<p>对 引用 进行的所有操作都是在与之绑定的对象上进行的:</p>
<ul>
<li>为 引用 赋值,实际上是把值赋给了 引用所绑定的对象</li>
<li>获取 引用 的值,实际上是获取 引用所绑定的对象 的值</li>
<li>以 引用 作为初始值,实际上是以 引用所绑定的对象 作为初始值</li>
</ul>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>refVal <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// 把 2 赋给 refVal 指向的对象,即,赋给 ival</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> ii <span class="token operator">=</span> refVal<span class="token punctuation">;</span> <span class="token comment">// 与 ii = ival 执行结果一样</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>refVal3 <span class="token operator">=</span> refVal<span class="token punctuation">;</span> <span class="token comment">// 正确:refVal3 绑定到了那个与 refVal 绑定的对象上,这里就是绑定到 ival 上</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> refVal<span class="token punctuation">;</span> <span class="token comment">// 正确:i 被初始化为 ival 的值</span></pre></td></tr></tbody></table></figure><p><strong>引用本身不是一个对象,所以不能定义引用的引用</strong></p>
<h3 id="引用的定义"><a class="anchor" href="#引用的定义">#</a> 引用的定义</h3>
<p>允许在一条语句中定义多个引用,其中,每个引用标识符都必须以符号 <code>&</code> 开头,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span><span class="token number">1024</span><span class="token punctuation">,</span> i2 <span class="token operator">=</span> <span class="token number">2048</span><span class="token punctuation">;</span> <span class="token comment">//i 和 i2 都是 int</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r <span class="token operator">=</span> i<span class="token punctuation">,</span> r2 <span class="token operator">=</span> i2<span class="token punctuation">;</span> <span class="token comment">//r 是 i 的引用,r2 是 int 型对象</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> i3 <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">,</span> <span class="token operator">&</span>ri <span class="token operator">=</span> i3<span class="token punctuation">;</span><span class="token comment">//i3 是 int 型对象,ri 是 i3 的引用</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r3 <span class="token operator">=</span> i3<span class="token punctuation">,</span> <span class="token operator">&</span>r4 <span class="token operator">=</span> i2<span class="token punctuation">;</span> <span class="token comment">//r3 和 r4 都是引用</span></pre></td></tr></tbody></table></figure><p>除了两种例外情况,其他所有 引用的类型 都要严格匹配于 与之绑定的对象</p>
<p>并且,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>refVal4 <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token comment">// 错误:引用类型的初始值必须是一个对象,而不能是字面值</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">double</span> dval <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>refVal5 <span class="token operator">=</span> dval<span class="token punctuation">;</span> <span class="token comment">// 错误:此处引用类型的初始值必须是 int 型对象</span></pre></td></tr></tbody></table></figure><h2 id="指针"><a class="anchor" href="#指针">#</a> 指针</h2>
<p>指针(pointer)是 “指向(point to)” 另外一种类型的复合类型</p>
<p>与引用类似,指针也可实现对其他对象的间接访问</p>
<p>与引用不同的是:</p>
<ol>
<li>指针本身是一个对象,允许对指针赋值和拷贝</li>
<li>指针可以先后指向不同的对象</li>
<li>指针无须在定义时赋初值</li>
</ol>
<blockquote>
<p>如果在块作用域内定义一个指针,但没有初始化指针,指针将拥有一个不确定的值</p>
</blockquote>
<p>通过将声明符写成 <code>*d</code> 的形式来定义指针,其中 <code>d</code> 是变量名</p>
<p>如果在一条语句中定义多个指针变量,每个变量前面都必须有符号 <code>*</code> ,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>ip1<span class="token punctuation">,</span> <span class="token operator">*</span>ip2<span class="token punctuation">;</span> <span class="token comment">//ip1 和 ip2 都是指向 int 型对象的指针</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">double</span> dp<span class="token punctuation">,</span> <span class="token operator">*</span>dp2<span class="token punctuation">;</span> <span class="token comment">//dp2 是指向 double 型对象的指针,dp 是 double 型对象</span></pre></td></tr></tbody></table></figure><h3 id="获取对象的地址"><a class="anchor" href="#获取对象的地址">#</a> 获取对象的地址</h3>
<p>指针存放的是某个对象的地址</p>
<p>要获取某对象的地址,需使用 <strong>取地址符( <code>&</code> 操作符)</strong></p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> ival <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>ival<span class="token punctuation">;</span> <span class="token comment">//p 存放变量 ival 的地址,或者说,p 是指向变量 ival 的指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token comment">// 把 p 定义为一个指向 int 的指针,随后初始化 p 令其指向名为 ival 的 int 对象</span></pre></td></tr></tbody></table></figure><p>引用不是对象,没有实际地址,因此,<strong>不能定义指向引用的指针</strong></p>
<p>除两种特殊情况外,其他所有 指针的类型 都要和 它所指向的对象 严格匹配,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">double</span> dval<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">double</span> <span class="token operator">*</span>pd <span class="token operator">=</span> <span class="token operator">&</span>dval<span class="token punctuation">;</span> <span class="token comment">// 正确:初始值是 double 型对象的地址</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">double</span> <span class="token operator">*</span>pd2 <span class="token operator">=</span> pd<span class="token punctuation">;</span> <span class="token comment">// 正确:初始值是 double 对象的指针</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>pi <span class="token operator">=</span> pd<span class="token punctuation">;</span> <span class="token comment">// 错误:指针 pi 的类型和 pd 的类型不匹配</span></pre></td></tr><tr><td data-num="6"></td><td><pre>pi <span class="token operator">=</span> <span class="token operator">&</span>dval<span class="token punctuation">;</span> <span class="token comment">// 错误:试图把 double 型对象的地址赋给 int 型指针</span></pre></td></tr></tbody></table></figure><p>实际上,<strong>在声明语句中,指针的类型 用于确定 指针所指对象的类型</strong> 。因此,二者必须匹配</p>
<h3 id="指针值"><a class="anchor" href="#指针值">#</a> 指针值</h3>
<p>指针的值(即地址),应属下列 4 种状态之一:</p>
<ol>
<li>指向一个对象</li>
<li>指向紧邻对象所占空间的下一个位置</li>
<li>空指针,即,指针没有指向任何对象</li>
<li>无效指针,即,上述情况之外的其他值</li>
</ol>
<p>拷贝 / 访问无效指针的值将会引发错误</p>
<h3 id="利用指针访问对象"><a class="anchor" href="#利用指针访问对象">#</a> 利用指针访问对象</h3>
<p>如果指针指向了一个对象,则允许使用 <strong>解引用符( <code>*</code> 操作符)</strong>,来访问该对象</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> ival <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>ival<span class="token punctuation">;</span> <span class="token comment">//p 是指向变量 ival 的指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre>cout <span class="token operator"><<</span> <span class="token operator">*</span>p<span class="token punctuation">;</span> <span class="token comment">// 解引用,访问指针所指对象,输出 42</span></pre></td></tr></tbody></table></figure><p>对指针解引用可以得到指针所指的对象</p>
<p>因此,给解引用所得结果赋值,也就是给指针所指对象赋值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token operator">*</span>p <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 经由指针 p 为 p 所指对象(即,ival)赋值</span></pre></td></tr><tr><td data-num="2"></td><td><pre>cout <span class="token operator"><<</span> <span class="token operator">*</span>p<span class="token punctuation">;</span> <span class="token comment">// 输出 0</span></pre></td></tr></tbody></table></figure><p><strong>解引用操作仅适用于那些确实指向了某个对象的有效指针</strong></p>
<h3 id="空指针"><a class="anchor" href="#空指针">#</a> 空指针</h3>
<p>空指针(null pointer)不指向任何对象</p>
<p>生成空指针的方法:</p>
<pre><code>int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL; // 需要首先 #include cstdlib
</code></pre>
<p>获得空指针最直接的办法:用字面值 <code>nullptr</code> 来初始化指针</p>
<ul>
<li><code>nullptr</code> 是一种特殊类型的字面值,它可以被转换成任意其他的指针类型</li>
<li>C++ 程序中,<strong>建议使用 <code>nullptr</code> ,而尽量避免使用 <code>NULL</code> </strong></li>
</ul>
<p><strong>把 <code>int</code> 变量直接赋给指针是错误的操作,即使 <code>int</code> 变量的值恰好等于 0 也不行</strong></p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> zero <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>pi <span class="token operator">=</span> zero<span class="token punctuation">;</span> <span class="token comment">// 错误:不能把 int 变量直接赋给指针</span></pre></td></tr></tbody></table></figure><p>尽管 C++ 语法上规定 “指针无须在定义时初始化”,但是,使用未经初始化的指针很可能会在运行时引发错误</p>
<p>因此,建议初始化所有的指针,并且,尽量在定义对象之后才定义指向它的指针。如果确实不清楚指针应该指向何处,可以把它初始化为 <code>nullptr</code> 或者 <code>0</code></p>
<h3 id="赋值和指针"><a class="anchor" href="#赋值和指针">#</a> 赋值和指针</h3>
<p>指针和引用都能实现对其他对象的间接访问</p>
<ul>
<li>引用本身不是一个对象。一旦定义了引用,就无法再将其绑定到另外的对象,以后每次使用这个引用都是访问它最初绑定的那个对象</li>
<li>指针可以指向新的对象:给指针赋值就是令它存放一个新的地址,从而指向一个新的对象</li>
</ul>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>pi <span class="token operator">=</span> <span class="token operator">&</span>ival<span class="token punctuation">;</span> <span class="token comment">//pi 的值被改变,现在 pi 指向了 ival</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">*</span>pi <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//ival 的值被改变,指针 pi 并没有改变</span></pre></td></tr></tbody></table></figure><h3 id="其他指针操作"><a class="anchor" href="#其他指针操作">#</a> 其他指针操作</h3>
<p>判断 <code>true</code> 和 <code>false</code> :</p>
<ul>
<li>如果指针的值是 <code>0</code> ,即,空指针,对应的条件值取 <code>false</code></li>
<li>如果指针非空,对应的条件值是 <code>true</code></li>
</ul>
<p>对于两个类型相同的合法指针,可以用相等操作符( <code>==</code> )和不相等操作符( <code>!=</code> )来比较它们,其结果为布尔类型的值</p>
<ul>
<li>如果两个指针所存放的地址值相同,则它们相等</li>
<li>反之,它们不相等</li>
</ul>
<p>两个指针相等,对应有三种可能:</p>
<ul>
<li>两个指针都为空</li>
<li>两个指针指向同一个对象</li>
<li>两个指针都指向了同一个对象的下一地址</li>
</ul>
<blockquote>
<p>需要注意的是,一个指针指向某对象,同时,另一个指针指向另外对象的下一地址,此时也有可能出现这两个指针值相同的情况,即指针相等</p>
</blockquote>
<h3 id="void-指针"><a class="anchor" href="#void-指针">#</a> void * 指针</h3>
<p><code>void *</code> 是一种特殊的指针类型,可用于存放任意对象的地址</p>
<p>但是,我们无法直接确定该地址中到底是个什么类型的对象</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">double</span> obj <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">,</span> <span class="token operator">*</span>pd <span class="token operator">=</span> <span class="token operator">&</span>obj<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">void</span> <span class="token operator">*</span>pv <span class="token operator">=</span> <span class="token operator">&</span>obj<span class="token punctuation">;</span> <span class="token comment">// 正确:void * 能存放任意类型对象的地址</span></pre></td></tr><tr><td data-num="3"></td><td><pre>pv <span class="token operator">=</span> pd<span class="token punctuation">;</span> <span class="token comment">// 正确</span></pre></td></tr></tbody></table></figure><p><code>void *</code> 指针的作用:</p>
<ul>
<li>与别的指针比较</li>
<li>作为函数的输入或输出</li>
<li>赋给另外一个 <code>void *</code> 指针</li>
</ul>
<p>注意,我们并不知道这个对象到底是什么类型,所以,不能直接操作 <code>void *</code> 指针所指的对象(因为无法确定能在这个对象上做哪些操作)</p>
<blockquote>
<p>给定指针 <code>p</code> ,你能知道它是否指向了一个合法的对象吗?如果能,叙述判断的思路;如果不能,说明原因</p>
<ul>
<li>如果在定义 <code>p</code> 时曾用 <code>nullptr</code> 或者 <code>0</code> 初始化 <code>p</code> ,判断 <code>p</code> 是否指向合法的对象,只需把 <code>p</code> 作为 <code>if</code> 语句的条件即可,如果 <code>p</code> 的值是 <code>nullptr</code> ,则条件为假;反之,条件为真</li>
<li>如果没有注意 <code>p</code> 的初始化,可把 <code>if(p)</code> 置于 <code>try</code> 结构中,当程序块顺利执行时,表示 <code>p</code> 指向了合法的对象;当程序块出错跳转到 <code>catch</code> 语句时,表示 <code>p</code> 没有指向合法的对象</li>
</ul>
</blockquote>
<h2 id="复合类型的声明"><a class="anchor" href="#复合类型的声明">#</a> 复合类型的声明</h2>
<p>变量的定义包括:一个 <strong>基本数据类型</strong>(base type)、一组 <strong>声明符</strong>(declarator)</p>
<p>在同一条定义语句中,基本数据类型只能有一个,但声明符的形式却可以有好几种,即,一条定义语句可能定义出不同类型的变量,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">,</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">,</span> <span class="token operator">&</span>r <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">//i 是一个 int 型对象</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token comment">//p 是一个指向 int 型对象的指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token comment">//r 是一个 int 对象的引用</span></pre></td></tr></tbody></table></figure><p>上例中, <code>int</code> 是基本数据类型, <code>*</code> 和 <code>&</code> 是类型修饰符</p>
<p>类型修饰符与变量名共同组成声明符</p>
<h3 id="定义多个变量"><a class="anchor" href="#定义多个变量">#</a> 定义多个变量</h3>
<p>在定义语句中,<strong>类型修饰符( <code>*</code> 和 <code>&</code> )仅作用于紧随其后的单个变量</strong>,而不是作用于本次定义的全部变量</p>
<p>涉及指针或引用的声明,一般有两种写法:</p>
<ol>
<li>
<p>把类型修饰符和变量标识符写在一起,这种形式强调了变量所具有的复合类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>p1<span class="token punctuation">,</span> <span class="token operator">*</span>p2<span class="token punctuation">;</span> <span class="token comment">//p1 和 p2 都是指向 int 型对象的指针</span></pre></td></tr></tbody></table></figure></li>
<li>
<p>把修饰符和类型名写在一起,并且每条语句只定义一个变量,这种形式强调本次声明定义了一种复合类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span><span class="token operator">*</span> p1<span class="token punctuation">;</span> <span class="token comment">//p1 是指向 int 型对象的指针</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span><span class="token operator">*</span> p2<span class="token punctuation">;</span> <span class="token comment">//p2 是指向 int 型对象的指针</span></pre></td></tr></tbody></table></figure></li>
</ol>
<p>推荐采用第一种写法,即,<strong>将 <code>*</code> (或是 <code>&</code> )与变量名连在一起</strong></p>
<h3 id="指向指针的指针"><a class="anchor" href="#指向指针的指针">#</a> 指向指针的指针</h3>
<p>指针是内存中的对象,有自己的地址,因此,允许把指针的地址再存放到另一个指针当中</p>
<p>通过 <code>*</code> 的个数可以区分指针的级别</p>
<ul>
<li><code>**</code> 表示指向指针的指针</li>
<li><code>***</code> 表示指向指针的指针的指针</li>
<li>以此类推</li>
</ul>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> ival <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>pi <span class="token operator">=</span> <span class="token operator">&</span>ival<span class="token punctuation">;</span> <span class="token comment">//pi 指向一个 int 型的数</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span><span class="token operator">*</span>ppi <span class="token operator">=</span> <span class="token operator">&</span>pi<span class="token punctuation">;</span> <span class="token comment">//ppi 指向一个 int 型的指针</span></pre></td></tr></tbody></table></figure><p>下图描述了它们之间的关系:</p>
<p><img loading="lazy" data-src="Cpp2-%E5%8F%98%E9%87%8F%E5%92%8C%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B/2.webp" alt=""></p>
<p>解引用 <code>int</code> 型指针会得到一个 <code>int</code> 型的数,类似地,解引用指向指针的指针会得到一个指针</p>
<p>对于指针的指针而言,如果想要访问最原始的那个对象,需要对指针的指针做两次解引用,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>cout <span class="token operator"><<</span> ival<span class="token punctuation">;</span> <span class="token comment">// direct value</span></pre></td></tr><tr><td data-num="2"></td><td><pre>cout <span class="token operator"><<</span> <span class="token operator">*</span>pi<span class="token punctuation">;</span> <span class="token comment">// indirect value</span></pre></td></tr><tr><td data-num="3"></td><td><pre>cout <span class="token operator"><<</span> <span class="token operator">*</span><span class="token operator">*</span>ppi<span class="token punctuation">;</span> <span class="token comment">// doubly indirect value</span></pre></td></tr></tbody></table></figure><h3 id="指向指针的引用"><a class="anchor" href="#指向指针的引用">#</a> 指向指针的引用</h3>
<p>引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>p<span class="token punctuation">;</span> <span class="token comment">//p 是一个指向 int 型对象的指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span><span class="token operator">&</span>r <span class="token operator">=</span> p<span class="token punctuation">;</span> <span class="token comment">//r 是一个对指针 p 的引用,即,r 是 p 的别名</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>r <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">;</span> <span class="token comment">// 给 r 赋值一个地址,相当于给 p 赋值一个地址,即,令 p 指向 i</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">*</span>r <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 给 r 的解引用(也就是 p 指向的对象)赋值,即,给 i 赋值 0</span></pre></td></tr></tbody></table></figure><p>想要理解变量 <code>r</code> 的类型,最简单的办法是从右向左阅读</p>
<ul>
<li>离变量名最近的符号对变量的类型有最直接的影响, <code>int *&r = p;</code> 中的符号 <code>&</code> 说明 <code>r</code> 是一个引用</li>
<li>声明符的其余部分用以确定 <code>r</code> 引用的类型,此例中的符号 <code>*</code> 说明 <code>r</code> 引用的是一个指针</li>
<li>最后,基本数据类型 <code>int</code> 指出,指针指向一个 <code>int</code> 型对象,即, <code>r</code> 引用的是一个 <code>int</code> 指针</li>
</ul>
<p><strong>对于一条比较复杂的指针或引用的声明语句,从右向左阅读有助于弄清楚它的真实含义</strong></p>
<h1 id="const-限定符"><a class="anchor" href="#const-限定符">#</a> const 限定符</h1>
<h2 id="const"><a class="anchor" href="#const">#</a> const</h2>
<p>关键字 <code>const</code> 可以限定变量的类型,使得变量成为常量</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> bufSize <span class="token operator">=</span> <span class="token number">512</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>bufSize <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span> <span class="token comment">// error: attempt to write to const object</span></pre></td></tr></tbody></table></figure><p><strong> <code>const</code> 对象一旦创建后,其值就不能再改变,因此, <code>const</code> 对象必须初始化</strong></p>
<p><code>const</code> 对象的初始值可以是任意复杂的表达式</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token function">get_size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ok: initialized at run time</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">// ok: initialized at compile time</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> k<span class="token punctuation">;</span> <span class="token comment">// error: k is uninitialized const</span></pre></td></tr></tbody></table></figure><h3 id="初始化-和-const"><a class="anchor" href="#初始化-和-const">#</a> 初始化 和 const</h3>
<p>只能对 <code>const</code> 对象执行不改变其内容的操作</p>
<p>例如, <code>const int</code> 和普通的 <code>int</code> 一样,都能参与算术运算,也都能转换成一个布尔值,等等</p>
<p>再例如,可以利用 <code>const</code> 对象去初始化另一个对象</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> ci <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">// ok: the value in i is copied into ci</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> j <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// ok: the value in ci is copied into j</span></pre></td></tr></tbody></table></figure><p><strong>默认情况下, <code>const</code> 对象仅在文件内有效</strong>,当多个文件中出现了同名的 <code>const</code> 变量时,相当于在不同文件中分别定义了独立的变量</p>
<p>如果想要只在一个文件中定义 <code>const</code> ,而在其他多个文件中声明并使用它,解决方案为:对于 <code>const</code> 变量,不管是声明还是定义,都添加 <code>extern</code> 关键字</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// file_1.cc defines and initializes a const that is accessible to other files</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">extern</span> <span class="token keyword">const</span> <span class="token keyword">int</span> bufSize <span class="token operator">=</span> <span class="token function">fcn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// definition</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token comment">// file_1.h</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">extern</span> <span class="token keyword">const</span> <span class="token keyword">int</span> bufSize<span class="token punctuation">;</span> <span class="token comment">// declaration, same bufSize as defined in file_1.cc</span></pre></td></tr></tbody></table></figure><p>如上述程序所示, <code>file_1.cc</code> 定义并初始化了 <code>bufSize</code> 。因为这条语句包含了初始值,所以它是一次定义,然而,因为 <code>bufSize</code> 是一个常量,必须用 <code>extern</code> 加以限定使其被其他文件使用</p>
<p><code>file_1.h</code> 头文件中的声明也由 <code>extern</code> 做了限定,其作用是指明 <code>bufSize</code> 并非本文件所独有,它的定义将在别处出现</p>
<blockquote>
<p>任何包含了显式初始化的声明,就成为了定义</p>
</blockquote>
<p><strong>如果想在多个文件之间共享 <code>const</code> 对象,必须在变量的定义之前添加 <code>extern</code> 关键字</strong></p>
<h2 id="const-的引用"><a class="anchor" href="#const-的引用">#</a> const 的引用</h2>
<p>对常量的引用(reference to const): 把引用绑定到 <code>const</code> 对象上</p>
<p>对常量的引用不能被用于修改它所绑定的对象,即,不能够通过该引用修改对象的值</p>
<p>但实际上,可以通过其他途径修改该对象的值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> ci <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r1 <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// 正确:引用及其对应的对象都是常量</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token comment">// 不存在通过引用 r1 修改 ci 的风险,故而语法正确</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>r1 <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r2 <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// 错误:试图让一个非常量引用指向一个常量对象</span></pre></td></tr><tr><td data-num="7"></td><td><pre> <span class="token comment">// 存在通过 r2 修改 ci 的风险,故而语法错误</span></pre></td></tr></tbody></table></figure><p>术语:常量引用是对 <code>const</code> 的引用</p>
<ul>
<li>C++ 程序员们经常把词组 “对 const 的引用” 简称为 “常量引用”</li>
<li>严格来说,并不存在常量引用,因为引用不是一个对象</li>
</ul>
<p><strong>复合类型</strong> 中指出,引用的类型必须与其所引用对象的类型一致,但是有两个例外</p>
<ul>
<li>
<p>第一种例外情况:在初始化常量引用时,允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许将一个常量引用绑定到非常量的对象、字面值,甚至是一个一般表达式</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r1 <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">// 允许将 const int & 绑定到一个普通 int 对象上。仅仅是说,不能通过 r1 修改 i 的值</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r2 <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">// 正确:r2 是一个对 const 的引用。不存在通过 r2 修改 42 的风险</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r3 <span class="token operator">=</span> r1 <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// 正确:r3 是一个对 const 的引用。不存在通过 r3 修改 r1 * 2 的风险</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r4 <span class="token operator">=</span> r1 <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// 错误:r4 是一个普通的非常量引用。存在通过 r4 修改 r1 * 2 的风险</span></pre></td></tr></tbody></table></figure></li>
</ul>
<p>可以这样理解这一例外情形:将一个 <code>const</code> 引用与一个非常量对象绑定,仅仅是限制了 “不能通过该 const 引用修改对象的值” ,因此,不存在通过 const 引用修改对象值的风险,语法正确</p>
<p>对 const 的引用可能引用一个并非 const 的对象</p>
<p>必须认识到,<strong>常量引用仅限定了引用可参与的操作,并未限定引用所指对象本身是否为常量</strong></p>
<p>常量引用的对象也可能是个非常量,所以允许通过其他途径改变它的值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r1 <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">// 引用 r1 绑定对象 i</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r2 <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">// 常量引用 r2 绑定对象 i ,不允许通过 r2 修改 i 的值</span></pre></td></tr><tr><td data-num="4"></td><td><pre>r1 <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//r1 并非常量引用,可以通过 r1 修改 i 的值</span></pre></td></tr><tr><td data-num="5"></td><td><pre>r2 <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 错误:r2 是常量引用,不允许通过 r2 修改 i 的值</span></pre></td></tr><tr><td data-num="6"></td><td><pre>i <span class="token operator">=</span> <span class="token number">36</span><span class="token punctuation">;</span> <span class="token comment">//i 不是常量,可以直接为 i 赋值</span></pre></td></tr></tbody></table></figure><p>上例中, <code>r2</code> 是 <code>i</code> 的常量引用,但 <code>r1</code> 不是 <code>i</code> 的常量引用,因此,可以通过 <code>r1</code> 修改 <code>i</code> 的值,并且, <code>i</code> 本身不是常量,可以直接为 <code>i</code> 赋新值</p>
<h2 id="指针-与-const"><a class="anchor" href="#指针-与-const">#</a> 指针 与 const</h2>
<h3 id="指向常量的指针"><a class="anchor" href="#指向常量的指针">#</a> 指向常量的指针</h3>
<p>与引用一样,也可以令指针指向常量或非常量</p>
<p>指向常量的指针(pointer to const):不能用于改变其所指对象的值</p>
<p>如何定义一个指向常量的指针:</p>
<ul>
<li><code>*</code> 放在变量名之前</li>
<li>基本数据类型放在 <code>*</code> 之前</li>
<li><code>const</code> 放在基本数据类型之前</li>
</ul>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">double</span> <span class="token operator">*</span>cptr <span class="token operator">=</span> <span class="token operator">&</span>pi<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p><strong>要想存放常量对象的地址,只能使用指向常量的指针</strong></p>
<p>即,限定了 “不能通过该指针修改常量对象的值”</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">double</span> pi <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">;</span> <span class="token comment">//pi 是一个常量</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">double</span> <span class="token operator">*</span>ptr <span class="token operator">=</span> <span class="token operator">&</span>pi<span class="token punctuation">;</span> <span class="token comment">// 错误:ptr 是一个普通指针。存在通过解引用 *ptr 修改 pi 的值的风险,故语法错误</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">double</span> <span class="token operator">*</span>cptr <span class="token operator">=</span> <span class="token operator">&</span>pi<span class="token punctuation">;</span> <span class="token comment">// 正确: cptr 是一个指向常量的指针。不存在通过 *cptr 修改 pi 的值的风险,故语法正确</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">*</span>cptr <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">// 错误:cptr 是指向常量的指针,不能给 *cptr 赋值</span></pre></td></tr></tbody></table></figure><p><strong>复合类型</strong> 中指出,指针的类型必须与其所指对象的类型一致,但是有两个例外:</p>
<ul>
<li>
<p>第一种例外情况:允许让一个指向常量的指针指向一个非常量的对象</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">double</span> dval <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">;</span> <span class="token comment">//dval 是一个双精度浮点数,它的值可以改变</span></pre></td></tr><tr><td data-num="2"></td><td><pre>cptr <span class="token operator">=</span> <span class="token operator">&</span>dval<span class="token punctuation">;</span> <span class="token comment">// 正确,但是不能通过 cptr 修改 dval 的值</span></pre></td></tr></tbody></table></figure></li>
</ul>
<p>可以这样理解这一例外情形:将一个常量指针指向一个非常量的对象,仅仅是限制了 “不能通过该指针修改对象的值” ,因此,不存在通过常量指针修改对象值的风险,语法正确</p>
<p>和常量引用一样,<strong>指向常量的指针也没有规定其所指的对象必须是一个常量。指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变</strong></p>
<blockquote>
<p>试试这样想吧:所谓指向常量的指针或引用,不过是指针或引用 “自以为是” 罢了,它们觉得自己指向了常量,所以自觉地不去改变所指对象的值</p>
</blockquote>
<h3 id="const-指针常量指针"><a class="anchor" href="#const-指针常量指针">#</a> const 指针(常量指针)</h3>
<p>指针是对象,因此,允许把指针本身定为常量,即,常量指针(const 指针)</p>
<p>常量指针(const pointer)必须初始化,并且,一旦初始化完成,它的值(即,存放在指针中的那个地址)就不能再改变</p>
<p>换而言之,一旦常量指针初始化完成,就不能再令其指向新的对象</p>
<p>如何定义一个常量指针:</p>
<ul>
<li><code>const</code> 放在变量名之前</li>
<li><code>*</code> 放在 <code>const</code> 之前</li>
<li>基本数据类型放在 <code>*</code> 之前</li>
</ul>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span><span class="token keyword">const</span> curErr <span class="token operator">=</span> <span class="token operator">&</span>errNum<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>其中,把 <code>*</code> 放在 <code>const</code> 关键字之前,用以说明指针是一个常量,这样的书写形式隐含着一层意味,即,不变的是指针本身的值,而非指向的那个值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> errNum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span><span class="token keyword">const</span> curErr <span class="token operator">=</span> <span class="token operator">&</span>errNum<span class="token punctuation">;</span> <span class="token comment">//curErr 将一直指向 errNum</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">double</span> pi <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">double</span> <span class="token operator">*</span><span class="token keyword">const</span> pip <span class="token operator">=</span> <span class="token operator">&</span>pi<span class="token punctuation">;</span> <span class="token comment">//pi 是一个指向常量的常量指针</span></pre></td></tr></tbody></table></figure><blockquote>
<p>要想弄清楚这些声明的含义,最行之有效的办法是从右向左阅读</p>
</blockquote>
<p>对 <code>int *const curErr = &errNum;</code> 这一语句而言</p>
<ul>
<li>离 <code>curErr</code> 最近的符号是 <code>const</code> ,意味着 <code>curErr</code> 本身是一个常量对象,对象的类型由声明符的其余部分确定</li>
<li>声明符中的下一个符号是 <code>*</code> ,意思是 <code>curErr</code> 是一个常量指针</li>
<li>最后,基本数据类型 <code>int</code> 说明该常量指针指向的是一个 <code>int</code> 对象</li>
</ul>
<p>类似地,我们也能推断出, <code>pip</code> 是一个常量指针,它指向的对象是一个 <code>double</code> 型常量</p>
<p><strong>常量指针,只是说指针本身是一个常量,并不是说不能通过指针修改其所指对象的值</strong>,能否这样做完全依赖于所指对象的类型</p>
<p>在上述示例中</p>
<ul>
<li><code>pip</code> 是一个指向常量的常量指针,因此,不论是 <code>pip</code> 所指的对象值、还是 <code>pip</code> 自己存储的那个地址,都不能改变</li>
<li><code>curErr</code> 指向的是一个非常量的整数,因此,可以用 <code>curErr</code> 去修改 <code>errNum</code> 的值</li>
</ul>
<h2 id="顶层-const-与-底层-const"><a class="anchor" href="#顶层-const-与-底层-const">#</a> 顶层 const 与 底层 const</h2>
<p>指针本身是不是常量,以及指针所指的对象是不是一个常量,是两个相互独立的问题</p>
<p><strong>顶层 const</strong>(top-levelconst):指针本身是一个常量</p>
<p><strong>底层 const</strong>(low-level const):指针所指的对象是一个常量</p>
<p>更一般的说法:</p>
<ul>
<li>顶层 const 可以表示任意的对象是常量,并且,对任何数据类型的对象都适用,如算术类型、类、指针等</li>
<li>底层 const 则与指针和引用等复合类型的基本类型部分有关</li>
</ul>
<p>比较特殊的是,指针类型既可以是顶层 const ,也可以是底层 const ,即,指向常量的常量指针</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span><span class="token keyword">const</span> pi <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">;</span> <span class="token comment">// 顶层 const</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> ci <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">// 顶层 const</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">*</span>p2 <span class="token operator">=</span> <span class="token operator">&</span>ci<span class="token punctuation">;</span> <span class="token comment">// 底层 const</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">*</span><span class="token keyword">const</span> p3 <span class="token operator">=</span> p2<span class="token punctuation">;</span> <span class="token comment">// 底层 const,顶层 const</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// 底层 const</span></pre></td></tr></tbody></table></figure><p>当执行对象的拷贝操作时,顶层 const 不受影响(执行拷贝操作并不会改变顶层 const 对象的值,因此,拷入和拷出的对象是否是常量都没什么影响)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>i <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// 正确:拷贝对象 ci 的值,顶层 const 对此操作无影响</span></pre></td></tr><tr><td data-num="2"></td><td><pre>p2 <span class="token operator">=</span> p3<span class="token punctuation">;</span> <span class="token comment">// 正确:p2 和 p3 指向的对象类型相同,p3 顶层 const 的部分不影响</span></pre></td></tr></tbody></table></figure><p>当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层 const 资格,或者,两个对象的数据类型必须能够转换(一般来说,非常量可以转换成常量,反之则不行)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> p3<span class="token punctuation">;</span> <span class="token comment">// 错误:p3 包含底层 const 的定义,而 p 没有。存在通过 p 修改 p3 所指对象的值的风险,故语法错误</span></pre></td></tr><tr><td data-num="2"></td><td><pre>p2 <span class="token operator">=</span> p3<span class="token punctuation">;</span> <span class="token comment">// 正确:p2 和 p3 都是底层 const 。不存在通过 p2 修改 p3 所指对象的值的风险,故语法正确</span></pre></td></tr><tr><td data-num="3"></td><td><pre>p2 <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">;</span> <span class="token comment">// 正确:int * 能转换成 const int * 。p2 是一个指向常量的指针</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">int</span> <span class="token operator">&</span>r <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">// 错误:普通的 int & 不能绑定 int 常量。存在通过引用 r 修改常量 ci 值的风险,故语法错误</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">&</span>r2 <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">// 正确:const int & 可以跟一个普通 int 对象绑定。r2 是一个指向常量的指针</span></pre></td></tr></tbody></table></figure><h2 id="constexpr-和-常量表达式"><a class="anchor" href="#constexpr-和-常量表达式">#</a> constexpr 和 常量表达式</h2>
<p>常量表达式(const expression)是指值不会改变,并且在编译阶段就能得到计算结果的表达式</p>
<ul>
<li>字面值属于常量表达式</li>
<li>用常量表达式初始化的 <code>const</code> 对象也是常量表达式(编译器将在编译过程中把用常量表达式初始化的 <code>const</code> 对象替换成对应的值)</li>
</ul>
<p>一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> max_files <span class="token operator">=</span> <span class="token number">20</span><span class="token punctuation">;</span> <span class="token comment">// 是常量表达式</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> limit <span class="token operator">=</span> max_files <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 是常量表达式</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">int</span> staff_size <span class="token operator">=</span> <span class="token number">27</span><span class="token punctuation">;</span> <span class="token comment">// 不是常量表达式(值会改变)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> sz <span class="token operator">=</span> <span class="token function">get_size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 不是常量表达式(无法在编译阶段得到计算结果)</span></pre></td></tr></tbody></table></figure><h3 id="constexpr-变量"><a class="anchor" href="#constexpr-变量">#</a> constexpr 变量</h3>
<p>C++ 11 标准规定,允许将变量声明为 <code>constexpr</code> 类型,以便由编译器来验证变量的值是否是一个常量表达式</p>
<p>因此,声明为 <code>constexpr</code> 的量一定是一个常量,并且,必须用常量表达式来初始化</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">constexpr</span> <span class="token keyword">int</span> mf <span class="token operator">=</span> <span class="token number">20</span><span class="token punctuation">;</span> <span class="token comment">// 20 是常量表达式</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">constexpr</span> <span class="token keyword">int</span> limit <span class="token operator">=</span> mf <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">//mf + 1 是常量表达式</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">constexpr</span> <span class="token keyword">int</span> sz <span class="token operator">=</span> <span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 只有当 size 是一个 constexpr 函数时才正确</span></pre></td></tr></tbody></table></figure><p>虽然不能使用普通函数作为 <code>constexpr</code> 变量的初始值,但是,C++ 11 标准允许定义一种特殊的 <code>constexpr</code> 函数。其中,函数应该足够简单,以使得编译时就可以计算其结果。于是,就能用 <code>constexpr</code> 函数去初始化 <code>constexpr</code> 变量了(详见 <strong>constexpr 函数</strong> )</p>
<blockquote>
<p>Generally, it is a good idea to use constexpr for variables that you intend to use as constant expressions.</p>
</blockquote>
<h3 id="字面值类型"><a class="anchor" href="#字面值类型">#</a> 字面值类型</h3>
<p>常量表达式的值需要在编译时就得到计算,因此,对声明 <code>constexpr</code> 时用到的类型必须有所限制。由于这些类型一般比较简单,值也显而易见、容易得到,就把它们称为字面值类型(literal type)</p>
<p>算术类型、引用和指针都属于字面值类型</p>
<p>自定义类 <code>Sales_item</code> 、 IO 库、 <code>string</code> 类型不属于字面值类型,不能被定义成 <code>constexpr</code></p>
<p>尽管指针和引用都能定义成 <code>constexpr</code> ,但它们的初始值却受到严格限制</p>
<ul>
<li>一个 <code>constexpr</code> 指针的初始值必须是 <code>nullptr</code> 或者 <code>0</code> ,或者是存储于某个固定地址中的对象</li>
</ul>
<blockquote>
<ul>
<li>一般来说,定义在函数体内的变量并非存放在固定地址中,因此, <code>constexpr</code> 指针不能指向这样的变量</li>
<li>对于定义在所有函数体之外的对象,其地址固定不变,故而能用来初始化 <code>constexpr</code> 指针</li>
<li>此外,允许函数定义一类有效范围超出函数本身的变量,这类变量和定义在函数体之外的变量一样,也有固定地址</li>
</ul>
</blockquote>
<h3 id="指针-和-constexpr"><a class="anchor" href="#指针-和-constexpr">#</a> 指针 和 constexpr</h3>
<p>如果在 <code>constexpr</code> 声明中定义了一个指针,限定符 <code>constexpr</code> 仅对指针有效,与指针所指的对象无关</p>
<p>相当于一个常量指针,表示指针本身是常量,无法指向别的对象</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span> <span class="token comment">// 指向常量的指针</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> <span class="token keyword">const</span> <span class="token operator">*</span>q1 <span class="token operator">=</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span> <span class="token comment">// 常量指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">constexpr</span> <span class="token keyword">int</span> <span class="token operator">*</span>q2 <span class="token operator">=</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span> <span class="token comment">// 常量指针</span></pre></td></tr></tbody></table></figure><p>关键在于, <code>constexpr</code> 把它所定义的对象置为了顶层 const</p>
<p>与其他常量指针类似, <code>constexpr</code> 指针既可以指向常量也可以指向一个非常量</p>
<h1 id="处理类型"><a class="anchor" href="#处理类型">#</a> 处理类型</h1>
<h2 id="类型别名"><a class="anchor" href="#类型别名">#</a> 类型别名</h2>
<p>类型别名(type alias):某种类型的同义词</p>
<p>有两种方法可用于定义类型别名:</p>
<ol>
<li>
<p>使用关键字 <code>typedef</code> :</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">typedef</span> <span class="token keyword">double</span> wages<span class="token punctuation">;</span> <span class="token comment">//wages 是 double 的同义词</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">typedef</span> wages base<span class="token punctuation">,</span> <span class="token operator">*</span>p<span class="token punctuation">;</span> <span class="token comment">//base 是 double 的同义词,p 是 double * 的同义词</span></pre></td></tr></tbody></table></figure><p>其中,关键字 <code>typedef</code> 作为声明语句中的基本数据类型的一部分出现</p>
<p>含有 <code>typedef</code> 的声明语句,其定义的不再是变量,而是类型别名</p>
<p>这里的声明符可以包含类型修饰符,因此,可以由基本数据类型构造出复合类型</p>
</li>
<li>
<p>使用别名声明(aliasdeclaration):</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">using</span> SI <span class="token operator">=</span> Sales_item<span class="token punctuation">;</span> <span class="token comment">// SI 是 Sales_item 的同义词</span></pre></td></tr></tbody></table></figure><p>用关键字 <code>using</code> 作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定为等号右侧类型的别名</p>
</li>
</ol>
<p>类型别名和类型的名字等价,只要是能使用类型名字的地方,就能使用类型别名</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>wages hourly<span class="token punctuation">,</span> weekly<span class="token punctuation">;</span> <span class="token comment">// 等效于 double hourly, weekly;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>SI item<span class="token punctuation">;</span> <span class="token comment">// 等效于 Sales_item item</span></pre></td></tr></tbody></table></figure><h3 id="指针-常量和类型别名"><a class="anchor" href="#指针-常量和类型别名">#</a> 指针、常量和类型别名</h3>
<p>如果某个类型别名指代的是复合类型或常量,对于使用类型别名来定义变量的声明语句而言,<strong>不能简单地用原复合类型来代替类型别名</strong></p>
<p>例如,以下声明语句用到了类型 <code>pstring</code> ,它实际上是类型 <code>char *</code> 的别名</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">typedef</span> <span class="token keyword">char</span> <span class="token operator">*</span>pstring<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> pstring cstr <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//cstr 是一个指向 char 对象的常量指针,相当于 char *const cstr = 0</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> pstring <span class="token operator">*</span>ps<span class="token punctuation">;</span> <span class="token comment">//ps 是一个指针,它的对象是一个指向 char 的常量指针</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token comment">// 因为 const pstring 是一个指向 char 的常量指针类型</span></pre></td></tr></tbody></table></figure><p>上述两条声明语句的基本数据类型都是 <code>const pstring</code> ,其中</p>
<ul>
<li><code>const</code> 是对 <code>pstring</code> 类型的修饰,用以说明 <code>pstring</code> 类型的变量 <code>cstr</code> 是常量</li>
<li><code>pstring</code> 是类型 <code>char *</code> 的别名,即,指向 <code>char</code> 的指针</li>
</ul>
<p>因此, <code>const pstring</code> 是指向 <code>char</code> 的常量指针,而非指向常量字符的指针(若直接用 <code>char *</code> 替换掉 <code>pstring</code> ,会把 <code>cstr</code> 误解成一个指向常量的指针)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> pstring cstr <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//cstr 是指向 char 的常量指针</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>cstr <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 对 const pstring cstr 的错误理解,将 cstr 错当成一个指向 char 常量的指针</span></pre></td></tr></tbody></table></figure><h2 id="auto-类型说明符"><a class="anchor" href="#auto-类型说明符">#</a> auto 类型说明符</h2>
<p>C++11 新标准引入了 <code>auto</code> 类型说明符</p>
<p><code>auto</code> 类型说明符:让编译器通过初始值来推算变量的类型</p>
<p><strong> <code>auto</code> 定义的变量必须有初始值</strong></p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// 根据 val1 和 val2 相加的结果来推断 item 的类型</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> item <span class="token operator">=</span> val1 <span class="token operator">+</span> val2<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>使用 <code>auto</code> 时,可以在一条语句中声明多个变量。但是需要注意,使用 <code>auto</code> 的声明语句只能有一个基本数据类型,即,该语句中所有变量初始值的基本数据类型必须一样</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">auto</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">;</span> <span class="token comment">//ok: i 是一个 int 型变量,p 是一个指向 int 型对象的指针</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> sz <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> pi <span class="token operator">=</span> <span class="token number">3.14</span><span class="token punctuation">;</span> <span class="token comment">//error: sz 和 pi 的类型不一致,根据 sz 会推导为 int 型,但 pi 会推断为 float/double 型</span></pre></td></tr></tbody></table></figure><h3 id="复合类型-常量-和-auto"><a class="anchor" href="#复合类型-常量-和-auto">#</a> 复合类型、常量 和 auto</h3>
<p>编译器推断出来的 <code>auto</code> 类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则</p>
<ol>
<li>
<p>当引用被用作初始值时,真正参与初始化的其实是引用对象的值,编译器以引用对象的类型作为 <code>auto</code> 的类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>r <span class="token operator">=</span> i<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> a <span class="token operator">=</span> r<span class="token punctuation">;</span> <span class="token comment">//a 是 int 型(r 是 int 型对象 i 的引用)</span></pre></td></tr></tbody></table></figure></li>
<li>
<p><code>auto</code> 一般会忽略掉顶层 <code>const</code> ,而底层 <code>const</code> 则会保留下来</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> ci <span class="token operator">=</span> i<span class="token punctuation">,</span> <span class="token operator">&</span>cr <span class="token operator">=</span> ci<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> b <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">//b 是一个 int 型(顶层 const 被忽略)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">auto</span> c <span class="token operator">=</span> cr<span class="token punctuation">;</span> <span class="token comment">//c 是一个 int 型</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">auto</span> d <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">;</span> <span class="token comment">//d 是一个指向 int 的指针</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">auto</span> e <span class="token operator">=</span> <span class="token operator">&</span>ci<span class="token punctuation">;</span> <span class="token comment">//e 是一个指向 int 常量的指针(底层 const 被保留)</span></pre></td></tr></tbody></table></figure></li>
<li>
<p>如果希望推断出的 <code>auto</code> 类型是一个顶层 <code>const</code> ,需要在 <code>auto</code> 前添加 <code>const</code> 限定符以明确指出</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">auto</span> f <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">//ci 的类型是 int,f 是一个 int 常量(顶层 const)</span></pre></td></tr></tbody></table></figure></li>
<li>
<p>可以将引用的类型设为 <code>auto</code> ,此时,引用的初始化规则仍然适用</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">auto</span> <span class="token operator">&</span>g <span class="token operator">=</span> ci<span class="token punctuation">;</span> <span class="token comment">//g is a const int&. 因为 ci 是 int 常量,不允许存在通过 g 修改 ci 的风险,故而 g 是常量引用</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> <span class="token operator">&</span>h <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">//error: 不能为非常量引用绑定字面值。字面值常量 42 的类型为 int,故 auto 推断为 int 型,此时存在通过 h 修改 42 的风险,故而语法错误</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">auto</span> <span class="token operator">&</span>j <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token comment">//ok: 可以为常量引用绑定字面值</span></pre></td></tr></tbody></table></figure><p>设置一个类型为 <code>auto</code> 的引用时,初始值中的顶层 <code>const</code> 属性仍然保留。即,不能通过引用修改所指对象的值</p>
</li>
<li>
<p>在一条语句中定义多个变量时,切记,符号 <code>&</code> 和 <code>*</code> 只从属于某个声明符,而非基本数据类型的一部分。因此,初始值必须是同一种类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">auto</span> k <span class="token operator">=</span> ci<span class="token punctuation">,</span> <span class="token operator">&</span>l <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token comment">//k 是 int 型对象,l 是 int 型对象 i 的引用</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">auto</span> <span class="token operator">&</span>m <span class="token operator">=</span> ci<span class="token punctuation">,</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>ci<span class="token punctuation">;</span> <span class="token comment">//m 是一个 int 型常量引用,p 是指向 int 常量的指针</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">auto</span> <span class="token operator">&</span>n <span class="token operator">=</span> i<span class="token punctuation">,</span> <span class="token operator">*</span>p2 <span class="token operator">=</span> <span class="token operator">&</span>ci<span class="token punctuation">;</span> <span class="token comment">//error: 根据 i 推断的类型是 int; 根据 & amp;ci 推断的类型是 const int。(存在通过 p2 修改 ci 的风险)</span></pre></td></tr></tbody></table></figure></li>
</ol>
<h2 id="decltype-类型指示符"><a class="anchor" href="#decltype-类型指示符">#</a> decltype 类型指示符</h2>
<p>C++11 新标准引入了类型说明符 <code>decltype</code> ,它的作用是选择并返回操作数的数据类型</p>
<p>编译器分析表达式并得到它的类型,却不实际计算表达式的值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> sum <span class="token operator">=</span> x<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>编译器并不实际调用函数 <code>f</code> ,而是使用 当调用发生时 <code>f</code> 的返回值类型 作为 <code>sum</code> 的类型</p>
<p>如果 <code>decltype</code> 使用的表达式是一个变量,则 <code>decltype</code> 返回该变量的类型(包括顶层 const 和引用在内)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">const</span> <span class="token keyword">int</span> ci <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span>cj <span class="token operator">=</span> ci<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span>ci<span class="token punctuation">)</span> x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">//x 的类型是 const int</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span>cj<span class="token punctuation">)</span> y <span class="token operator">=</span> x<span class="token punctuation">;</span> <span class="token comment">//y 的类型是 const int &</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span>cj<span class="token punctuation">)</span> z<span class="token punctuation">;</span> <span class="token comment">//error: z 是一个引用,必须初始化</span></pre></td></tr></tbody></table></figure><blockquote>
<p>需要指出的是,引用从来都作为其所指对象的同义词出现,只有用在 <code>decltype</code> 处是一个例外:在 <code>decltype</code> 中,引用和引用所指对象是不同的数据类型,如上例, <code>ci</code> 是 <code>const int</code> 型,而 <code>cj</code> 是 <code>const int&</code> 型</p>
</blockquote>
<h3 id="decltype-和-引用"><a class="anchor" href="#decltype-和-引用">#</a> decltype 和 引用</h3>
<p>如果 <code>decltype</code> 使用的表达式不是一个变量,则 <code>decltype</code> 返回表达式结果对应的类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">//decltype 的结果可以是引用类型</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">,</span> <span class="token operator">*</span>p <span class="token operator">=</span> <span class="token operator">&</span>i<span class="token punctuation">,</span> <span class="token operator">&</span>r <span class="token operator">=</span> i<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span>r <span class="token operator">+</span> <span class="token number">0</span><span class="token punctuation">)</span> b<span class="token punctuation">;</span> <span class="token comment">//ok: 加法的结果是 int 型,故而 b 是一个(未初始化)的 int 对象</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span><span class="token operator">*</span>p<span class="token punctuation">)</span> c<span class="token punctuation">;</span> <span class="token comment">//error: c 是 int& 型,必须初始化</span></pre></td></tr></tbody></table></figure><p>因为 <code>r</code> 是一个引用,因此 <code>decltype(r)</code> 的结果是引用类型。如果想让结果类型是 <code>r</code> 所指对象的类型,可以把 <code>r</code> 作为表达式的一部分(例如, <code>r + 0</code> )</p>
<p>如果表达式执行的是解引用操作, <code>decltype</code> 将得到引用类型。解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此, <code>decltype(*p)</code> 的结果类型就是 <code>int&</code> ,而非 <code>int</code></p>
<p>如果 <code>decltype</code> 使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器会把它当成是一个表达式,这样的 <code>decltype</code> 会得到引用类型</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// decltype of a parenthesized variable is always a reference</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">)</span> d<span class="token punctuation">;</span> <span class="token comment">//error: d 是 int &,必须初始化</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">decltype</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> e<span class="token punctuation">;</span> <span class="token comment">//ok: e 是一个(未被初始化的)int</span></pre></td></tr></tbody></table></figure><p>换而言之</p>
<ul>
<li>
<p>对于 <code>decltype((variable))</code> (注意是双层括号),结果类型永远是引用类型</p>
</li>
<li>
<p>对于 <code>decltype(variable)</code> ,只有当 <code>variable</code> 本身就是一个引用时,结果类型才是引用类型</p>
</li>
</ul>
<h2 id="auto-和-decltype"><a class="anchor" href="#auto-和-decltype">#</a> auto 和 decltype</h2>
<p><code>auto</code> 和 <code>decltype</code> 的区别:</p>
<ol>
<li>
<p><code>auto</code> 类型说明符用编译器计算变量的初始值来推断其类型,而 <code>decltype</code> 虽然也让编译器分析表达式并得到它的类型,但是不实际计算表达式的值</p>
</li>
<li>
<p>编译器推断出来的 <code>auto</code> 类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。例如, <code>auto</code> 一般会忽略掉顶层 <code>const</code> ,而把底层 <code>const</code> 保留下来。与之相反, <code>decltype</code> 会保留变量的顶层 <code>const</code></p>
</li>
<li>
<p>与 <code>auto</code> 不同, <code>decltype</code> 的结果类型与表达式形式密切相关:如果 <code>decltype</code> 使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,则编译器将推断得到引用类型</p>
</li>
</ol>
<h1 id="自定义数据结构"><a class="anchor" href="#自定义数据结构">#</a> 自定义数据结构</h1>
<p>数据结构是 把一组相关的数据元素组织起来然后使用它们 的策略和方法</p>
<p>C++ 语言允许用户以类的形式自定义数据类型,而库类型 <code>string</code> 、 <code>istream</code> 、 <code>ostream</code> 等也都是以类的形式定义的</p>
<h2 id="定义-sales_data-类型"><a class="anchor" href="#定义-sales_data-类型">#</a> 定义 Sales_data 类型</h2>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token punctuation">{</span></pre></td></tr><tr><td data-num="3"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>这里的类以 <strong>关键字 <code>struct</code> </strong> 开始,紧跟着类名和类体(其中类体部分可以为空)</p>
<ul>
<li>类体由花括号包围形成了一个新的作用域</li>
<li>类内部定义的名字必须唯一,但是可以与类外部定义的名字重复</li>
</ul>
<p><strong>类体右侧的表示结束的花括号后必须写一个分号</strong>,这是因为类体后面可以紧跟变量名以示对该类型对象的定义</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span> accum<span class="token punctuation">,</span> trans<span class="token punctuation">,</span> <span class="token operator">*</span>salesptr<span class="token punctuation">;</span> <span class="token comment">// equivalent, but better way to define these objects</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>Sales_data accum<span class="token punctuation">,</span> trans<span class="token punctuation">,</span> <span class="token operator">*</span>salesptr<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>一般来说,最好不要把对象的定义和类的定义放在一起</p>
<p>也可以使用 C++ 语言提供的另外一个 <strong>关键字 <code>class</code> </strong> 来定义数据结构</p>
<p>类的定义可以与 <code>main</code> 函数放在同一个文件内。但是,我们通常在头文件中定义类,并且类所在头文件的名字应与类的名字一样</p>
<p>例如,库类型 <code>string</code> 在名为 <code>string</code> 的头文件中定义。又如,我们应该把 <code>Sales_data</code> 类定义在名为 <code>Sales_data.h</code> 的头文件中</p>
<h2 id="类数据成员"><a class="anchor" href="#类数据成员">#</a> 类数据成员</h2>
<p>类体定义类的成员,我们在上述例子中定义的类只有数据成员(datamember)</p>
<p>类的数据成员定义了类的对象的具体内容,每个对象有自己的一份数据成员拷贝。修改一个对象的数据成员,不会影响其他 <code>Sales_data</code> 的对象</p>
<p>定义数据成员的方法:</p>
<ul>
<li>首先说明一个基本类型</li>
<li>随后紧跟一个或多个声明符</li>
</ul>
<p>C++ 11 标准规定,可以为数据成员提供一个类内初始值(in-class initializer)</p>
<ul>
<li>创建对象时,类内初始值将用于初始化数据成员</li>
<li>没有初始值的成员将被默认初始化</li>
</ul>
<p>对类内初始值的限制:要么放在花括号里,要么放在等号右边,而不能使用圆括号</p>
<h2 id="使用-sales_data-类"><a class="anchor" href="#使用-sales_data-类">#</a> 使用 Sales_data 类</h2>
<p><a target="_blank" rel="noopener" href="https://weread.qq.com/web/reader/ff732fe072021a24ff7bb24k1ff325f02181ff1de7742fc">详见:自己动手实现对于 Sales_data 类的操作</a></p>
<p>关键在于,使用点操作符( <code>.</code> )读入对象的成员,如</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>Sales_data data1<span class="token punctuation">;</span> <span class="token comment">//data1 为 Sales_data 类型的对象</span></pre></td></tr><tr><td data-num="2"></td><td><pre>std<span class="token double-colon punctuation">::</span>cin <span class="token operator">>></span> data1<span class="token punctuation">.</span>bookNo <span class="token operator">>></span> data1<span class="token punctuation">.</span>units_sold<span class="token punctuation">;</span> <span class="token comment">// 写入对象 data1 的 bookNo 成员和 units_sold 成员</span></pre></td></tr></tbody></table></figure><h2 id="编写自己的头文件"><a class="anchor" href="#编写自己的头文件">#</a> 编写自己的头文件</h2>
<p><strong>类通常被定义在头文件中,并且,类所在头文件的名字应与类的名字一样</strong></p>
<p><strong>头文件通常包含那些只能被定义一次的实体</strong>,如:类、 <code>const</code> 和 <code>constexpr</code> 变量等</p>
<p>头文件也经常用到其他头文件的功能。因此,有必要在书写头文件时做适当处理,使其遇到多次包含的情况也能安全和正常地工作</p>
<blockquote>
<p>头文件一旦改变,相关的源文件必须重新编译以获取更新过的声明</p>
</blockquote>
<h3 id="预处理器概述"><a class="anchor" href="#预处理器概述">#</a> 预处理器概述</h3>
<p>确保头文件多次包含时仍能安全工作的常用技术是 <strong>预处理器</strong>(preprocessor)</p>
<p>预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。之前已经用到了一项预处理功能 <code>#include</code> ,当预处理器看到 <code>#include</code> 标记时就会用指定的头文件的内容代替 <code>#include</code></p>
<p>C++ 程序还会用到的一项预处理功能是 <strong>头文件保护符</strong>(headerguard),头文件保护符依赖于预处理变量</p>
<ul>
<li>预处理变量有两种状态:已定义和未定义</li>
<li><code>#define</code> 指令把一个名字设定为预处理变量</li>
<li><code>#ifdef</code> 和 <code>#ifdef</code> 两个指令分别检查某个指定的预处理变量是否已经定义
<ul>
<li><code>#ifdef</code> 指令:当且仅当变量已定义时为真</li>
<li><code>#ifndef</code> 指令:当且仅当变量未定义时为真</li>
</ul>
</li>
<li>一旦检查结果为真,则执行后续操作直至遇到 <code>#endif</code> 指令为止</li>
</ul>
<p>使用这些功能就能有效地防止重复包含的发生</p>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifndef</span> <span class="token expression">SALES_DATA_H </span><span class="token comment">// SALES_DATA_H 即为预处理变量,ifndef 是 if not defined 的缩写。若未定义 SALES_DATA_H,则执行后续操作,直到遇到 #endif 指令</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">SALES_DATA_H</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><string></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="5"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="7"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span></pre></td></tr></tbody></table></figure><p>第一次包含 <code>Sales_data.h</code> 时, <code>#ifndef</code> 的检查结果为真,预处理器将顺序执行后面的操作直至遇到 <code>#endif</code> 为止。此时,预处理变量 <code>SALES_DATA_H</code> 的值将变为已定义,而且 <code>Sales_data.h</code> 也会被拷贝到我们的程序中来。后面如果再一次包含 <code>Sales_data.h</code> ,则 <code>#ifndef</code> 的检查结果将为假,编译器将忽略 <code>#ifndef</code> 到 <code>#endif</code> 之间的部分</p>
<blockquote>
<p>预处理变量无视 C++ 语言中关于作用域的规则</p>
</blockquote>
<p>整个程序中的预处理变量包括头文件保护符必须唯一,<strong>通常的做法是基于头文件中类的名字来构建保护符的名字</strong>,以确保其唯一性。为了避免与程序中的其他实体发生名字冲突,<strong>一般把预处理变量的名字全部大写</strong></p>
<blockquote>
<p>注意:头文件即使(目前还)没有被包含在任何其他头文件中,也应该设置保护符。头文件保护符很简单,程序员只要习惯性地加上就可以了,没必要太在乎你的程序到底需不需要</p>
</blockquote>
<h1 id="术语表"><a class="anchor" href="#术语表">#</a> 术语表</h1>
<p><code>地址(address)</code> :是一个数字,根据它可以找到内存中的一个字节。</p>
<p><code>别名声明(alias declaration)</code> :为另外一种类型定义一个同义词:使用 “名字 = 类型” 的格式将名字作为该类型的同义词。</p>
<p><code>算术类型(arithmetic type)</code> :布尔值、字符、整数、浮点数等内置类型。</p>
<p><code>数组(array)</code> :是一种数据结构,存放着一组未命名的对象,可以通过索引来访问这些对象。</p>
<p><code>auto</code> :是一个类型说明符,通过变量的初始值来推断变量的类型。</p>
<p><code>基本类型(base type)</code> :是类型说明符,可用 <code>const</code> 修饰,在声明语句中位于声明符之前。基本类型提供了最常见的数据类型,以此为基础构建声明符。</p>
<p><code>绑定(bind)</code> :令某个名字与给定的实体关联在一起,使用该名字也就是使用该实体。例如,引用就是将某个名字与某个对象绑定在一起。</p>
<p><code>字节(byte)</code> :内存中可寻址的最小单元,大多数机器的字节占 8 位。</p>
<p><code>类成员(class member)</code> :类的组成部分。</p>
<p><code>复合类型(compound type)</code> :是一种类型,它的定义以其他类型为基础。</p>
<p><code>const</code> :是一种类型修饰符,用于说明永不改变的对象。 <code>const</code> 对象一旦定义就无法再赋新值,所以必须初始化。</p>
<p><code>常量指针(const pointer)</code> :是一种指针,它的值永不改变。</p>
<p><code>常量引用(const reference)</code> :是一种习惯叫法,含义是指向常量的引用。</p>
<p><code>常量表达式(const expression)</code> :能在编译时计算并获取结果的表达式。</p>
<p><code>constexpr</code> :是一种函数,用于代表一条常量表达式。</p>
<p><code>转换(conversion)</code> :一种类型的值转变成另外一种类型值的过程。C++ 语言支持内置类型之间的转换。</p>
<p><code>数据成员(data member)</code> :组成对象的数据元素,类的每个对象都有类的数据成员的一份拷贝。数据成员可以在类内部声明的同时初始化。</p>
<p><code>声明(declaration)</code> :声称存在一个变量、函数或是别处定义的类型。名字必须在定义或声明之后才能使用。</p>
<p><code>声明符(declarator)</code> :是声明的一部分,包括被定义的名字和类型修饰符,其中类型修饰符可以有也可以没有。</p>
<p><code>decltype</code> :是一个类型说明符,从变量或表达式推断得到类型。</p>
<p><code>默认初始化(default initialization)</code> :当对象未被显式地赋予初始值时执行的初始化行为。由类本身负责执行的类对象的初始化行为。全局作用域的内置类型对象初始化为 0;局部作用域的对象未被初始化即拥有未定义的值。</p>
<p><code>定义(definition)</code> :为某一特定类型的变量申请存储空间,可以选择初始化该变量。名字必须在定义或声明之后才能使用。</p>
<p><code>转义序列(escape sequence)</code> :字符特别是那些不可打印字符的替代形式。转义以反斜线开头,后面紧跟一个字符,或者不多于 3 个八进制数字,或者字母 x 加上 1 个十六进制数。</p>
<p><code>全局作用域(global scope)</code> :位于其他所有作用域之外的作用域。</p>
<p><code>头文件保护符(header guard)</code> :使用预处理变量以防止头文件被某个文件重复包含。</p>
<p><code>标识符(identifier)</code> :组成名字的字符序列,标识符对大小写敏感。</p>
<p><code>类内初始值(in-class initializer)</code> :在声明类的数据成员时同时提供的初始值,必须置于等号右侧或花括号内。</p>
<p><code>在作用域内(in scope)</code> :名字在当前作用域内可见。</p>
<p><code>被初始化(initialized)</code> :变量在定义的同时被赋予初始值,变量一般都应该被初始化。</p>
<p><code>内层作用域(inner scope)</code> :嵌套在其他作用域之内的作用域。</p>
<p><code>整型(integral type)</code> :参见算术类型。</p>
<p><code>列表初始化(list initialization)</code> :利用花括号把一个或多个初始值放在一起的初始化形式。</p>
<p><code>字面值(literal)</code> :是一个不能改变的值,如数字、字符、字符串等。单引号内的是字符字面值,双引号内的是字符串字面值。</p>
<p><code>局部作用域(local scope)</code> :是块作用域的习惯叫法。</p>
<p><code>底层const(low-level const)</code> :一个不属于顶层的 <code>const</code> ,类型如果由底层常量定义,则不能被忽略。</p>
<p><code>成员(member)</code> :类的组成部分。</p>
<p><code>不可打印字符(nonprintable character)</code> :不具有可见形式的字符,如控制符、退格、换行符等。</p>
<p><code>空指针(null pointer)</code> :值为 0 的指针,空指针合法但是不指向任何对象。</p>
<p><code>nullptr</code> :是表示空指针的字面值常量。</p>
<p><code>对象(object)</code> :是内存的一块区域,具有某种类型,变量是命名了的对象。</p>
<p><code>外层作用域(outer scope)</code> :嵌套着别的作用域的作用域。</p>
<p><code>指针(pointer)</code> :是一个对象,存放着某个对象的地址,或者某个对象存储区域之后的下一地址,或者 0。</p>
<p><code>指向常量的指针(pointer to const)</code> :是一个指针,存放着某个常量对象的地址。指向常量的指针不能用来改变它所指对象的值。</p>
<p><code>预处理器(preprocessor)</code> :在 C++ 编译过程中执行的一段程序。</p>
<p><code>预处理变量(preprocessor variable)</code> :由预处理器管理的变量。在程序编译之前,预处理器负责将程序中的预处理变量替换成它的真实值。</p>
<p><code>引用(reference)</code> :是某个对象的别名。</p>
<p><code>对常量的引用(reference to const)</code> :是一个引用,不能用来改变它所绑定对象的值。对常量的引用可以绑定常量对象,或者非常量对象,或者表达式的结果。</p>
<p><code>作用域(scope)</code> :是程序的一部分,在其中某些名字有意义。</p>
<p>C++ 的作用域:全局、类、块</p>
<ul>
<li>
<p><code>全局(global)</code> —— 名字定义在所有其他作用域之外。</p>
</li>
<li>
<p><code>类(class)</code> —— 名字定义在类内部。命名空间(namespace)—— 名字定义在命名空间内部。</p>
</li>
<li>
<p><code>块(block)</code> —— 名字定义在块内部。名字从声明位置开始直至声明语句所在的作用域末端为止都是可用的。</p>
</li>
</ul>
<p><code>分离式编译(separate compilation)</code> :把程序分割为多个单独文件的能力。</p>
<p><code>带符号类型(signed)</code> :保存正数、负数或 0 的整型。</p>
<p><code>字符串(string)</code> :是一种库类型,表示可变长字符序列。</p>
<p><code>struct</code> :是一个关键字,用于定义类。</p>
<p><code>临时值(temporary)</code> :编译器在计算表达式结果时创建的无名对象。为某表达式创建了一个临时值,则此临时值将一直存在直到包含有该表达式的最大的表达式计算完成为止。</p>
<p><code>顶层const(top-level const)</code> :是一个 <code>const</code> ,规定某对象的值不能改变。</p>
<p><code>类型别名(type alias)</code> :是一个名字,是另外一个类型的同义词,通过关键字 typedef 或别名声明语句来定义。</p>
<p><code>类型检查(type checking)</code> :是一个过程,编译器检查程序使用某给定类型对象的方式与该类型的定义是否一致。</p>
<p><code>类型说明符(type specifier)</code> :类型的名字。</p>
<p><code>typedef</code> :为某类型定义一个别名。当关键字 <code>typedef</code> 作为声明的基本类型出现时,声明中定义的名字就是类型名。</p>
<p><code>未定义(undefined)</code> :即 C++ 语言没有明确规定的情况。不论是否有意为之,未定义行为都可能引发难以追踪的运行时错误、安全问题和可移植性问题。</p>
<p><code>未初始化(uninitialized)</code> :变量已定义但未被赋予初始值。一般来说,试图访问未初始化变量的值将引发未定义行为。</p>
<p><code>无符号类型(unsigned)</code> :保存大于等于 0 的整型。</p>
<p><code>变量(variable)</code> :命名的对象或引用。C++ 语言要求变量要先声明后使用。</p>
<p><code>void*</code> :可以指向任意非常量的指针类型,不能执行解引用操作。</p>
<p><code>void类型</code> :是一种有特殊用处的类型,既无操作也无值。不能定义一个 <code>void</code> 类型的变量。</p>
<p><code>字(word)</code> :在指定机器上进行整数运算的自然单位。一般来说,字的空间足够存放地址。32 位机器上的字通常占据 4 个字节。</p>
<p><code>&运算符(&operator)</code> :取地址运算符。</p>
<p><code>*运算符(* operator)</code> :解引用运算符。解引用一个指针将返回该指针所指的对象,为解引用的结果赋值也就是为指针所指的对象赋值。</p>
<p><code>#define</code> :是一条预处理指令,用于定义一个预处理变量。</p>
<p><code>#endif</code> :是一条预处理指令,用于结束一个 <code>#ifdef</code> 或 <code>#ifndef</code> 区域。</p>
<p><code>#ifdef</code> :是一条预处理指令,用于判断给定的变量是否已经定义。</p>
<p><code>#ifndef</code> :是一条预处理指令,用于判断给定的变量是否尚未定义。</p>
<p>参考:C++ Primer 中文版(第 5 版)</p>
</div><footer><div class="meta"><span class="item"><span class="icon"><i class="ic i-calendar-check"></i></span><span class="text">更新于</span><time title="修改时间:2024-06-08 23:07:52" itemprop="dateModified" datetime="2024-06-08T23:07:52+08:00">2024-06-08</time></span></div><div id="copyright"><ul><li class="author"><strong>本文作者:</strong>Jiankychen<i class="ic i-at"><em>@</em></i>Jiankychen's Blog</li><li class="link"><strong>本文链接:</strong><a href="https://jiankychen.github.io/cpp-variables.html" title="C++ 变量和基本类型">https://jiankychen.github.io/cpp-variables.html</a></li><li class="license"><strong>版权声明:</strong>本站所有文章除特别声明外,均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh"><i class="ic i-creative-commons"><em>(CC)</em></i>BY-NC-SA</a> 许可协议。转载请注明出处!</li></ul></div></footer></article></div><div class="post-nav"><div class="item left"><a href="/introduce-cpp.html" rel="prev" itemprop="url" data-background-image="https://i.imgtg.com/2023/03/09/Y0iNK.jpg" title="初识 C++"><span class="type">上一篇</span><span class="category"><i class="ic i-flag"></i>C++</span><h3>初识 C++</h3></a></div><div class="item right"><a href="/cpp-vectors.html" rel="next" itemprop="url" data-background-image="https://img.timelessq.com/images/2022/07/26/488297bfd0233b6c6a444f1860e55d45.jpg" title="C++ 字符串、向量和数组"><span class="type">下一篇</span><span class="category"><i class="ic i-flag"></i>C++</span><h3>C++ 字符串、向量和数组</h3></a></div></div><div class="wrap" id="comments"></div></div><div id="sidebar"><div class="inner"><div class="panels"><div class="inner"><div class="contents panel pjax" data-title="文章目录"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E5%86%85%E7%BD%AE%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.</span> <span class="toc-text"> 基本内置类型</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%AE%97%E6%9C%AF%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.1.</span> <span class="toc-text"> 算术类型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B4%E5%9E%8B%E4%B8%8E%E6%B5%AE%E7%82%B9%E5%9E%8B"><span class="toc-number">1.1.1.</span> <span class="toc-text"> 整型与浮点型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B8%A6%E7%AC%A6%E5%8F%B7%E7%B1%BB%E5%9E%8B%E5%92%8C%E6%97%A0%E7%AC%A6%E5%8F%B7%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.1.2.</span> <span class="toc-text"> 带符号类型和无符号类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.1.3.</span> <span class="toc-text"> 如何选择类型</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2"><span class="toc-number">1.2.</span> <span class="toc-text"> 类型转换</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BD%AC%E6%8D%A2%E8%A7%84%E5%88%99%E7%AE%80%E4%BB%8B"><span class="toc-number">1.2.1.</span> <span class="toc-text"> 转换规则(简介)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%AB%E6%9C%89%E6%97%A0%E7%AC%A6%E5%8F%B7%E7%B1%BB%E5%9E%8B%E7%9A%84%E8%A1%A8%E8%BE%BE%E5%BC%8F"><span class="toc-number">1.2.2.</span> <span class="toc-text"> 含有无符号类型的表达式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AD%97%E9%9D%A2%E5%80%BC%E5%B8%B8%E9%87%8F"><span class="toc-number">1.3.</span> <span class="toc-text"> 字面值常量</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B4%E5%9E%8B%E5%92%8C%E6%B5%AE%E7%82%B9%E5%9E%8B%E5%AD%97%E9%9D%A2%E5%80%BC"><span class="toc-number">1.3.1.</span> <span class="toc-text"> 整型和浮点型字面值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E5%80%BC"><span class="toc-number">1.3.2.</span> <span class="toc-text"> 字符和字符串字面值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97"><span class="toc-number">1.3.3.</span> <span class="toc-text"> 转义序列</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E5%AE%9A%E5%AD%97%E9%9D%A2%E5%80%BC%E7%9A%84%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.3.4.</span> <span class="toc-text"> 指定字面值的类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B8%83%E5%B0%94%E5%AD%97%E9%9D%A2%E5%80%BC%E5%92%8C%E6%8C%87%E9%92%88%E5%AD%97%E9%9D%A2%E5%80%BC"><span class="toc-number">1.3.5.</span> <span class="toc-text"> 布尔字面值和指针字面值</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8F%98%E9%87%8F"><span class="toc-number">2.</span> <span class="toc-text"> 变量</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%98%E9%87%8F%E5%AE%9A%E4%B9%89"><span class="toc-number">2.1.</span> <span class="toc-text"> 变量定义</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-number">2.1.1.</span> <span class="toc-text"> 初始化</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%97%E8%A1%A8%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-number">2.1.1.1.</span> <span class="toc-text"> 列表初始化</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%BB%98%E8%AE%A4%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-number">2.1.1.2.</span> <span class="toc-text"> 默认初始化</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E"><span class="toc-number">2.2.</span> <span class="toc-text"> 变量声明</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%87%E8%AF%86%E7%AC%A6"><span class="toc-number">2.3.</span> <span class="toc-text"> 标识符</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%90%8D%E5%AD%97%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">2.4.</span> <span class="toc-text"> 名字的作用域</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%A4%8D%E5%90%88%E7%B1%BB%E5%9E%8B"><span class="toc-number">3.</span> <span class="toc-text"> 复合类型</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%95%E7%94%A8"><span class="toc-number">3.1.</span> <span class="toc-text"> 引用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%95%E7%94%A8%E5%8D%B3%E5%88%AB%E5%90%8D"><span class="toc-number">3.1.1.</span> <span class="toc-text"> 引用即别名</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%95%E7%94%A8%E7%9A%84%E5%AE%9A%E4%B9%89"><span class="toc-number">3.1.2.</span> <span class="toc-text"> 引用的定义</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8C%87%E9%92%88"><span class="toc-number">3.2.</span> <span class="toc-text"> 指针</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%9C%B0%E5%9D%80"><span class="toc-number">3.2.1.</span> <span class="toc-text"> 获取对象的地址</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E9%92%88%E5%80%BC"><span class="toc-number">3.2.2.</span> <span class="toc-text"> 指针值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%A9%E7%94%A8%E6%8C%87%E9%92%88%E8%AE%BF%E9%97%AE%E5%AF%B9%E8%B1%A1"><span class="toc-number">3.2.3.</span> <span class="toc-text"> 利用指针访问对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A9%BA%E6%8C%87%E9%92%88"><span class="toc-number">3.2.4.</span> <span class="toc-text"> 空指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B5%8B%E5%80%BC%E5%92%8C%E6%8C%87%E9%92%88"><span class="toc-number">3.2.5.</span> <span class="toc-text"> 赋值和指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B6%E4%BB%96%E6%8C%87%E9%92%88%E6%93%8D%E4%BD%9C"><span class="toc-number">3.2.6.</span> <span class="toc-text"> 其他指针操作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#void-%E6%8C%87%E9%92%88"><span class="toc-number">3.2.7.</span> <span class="toc-text"> void * 指针</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%A4%8D%E5%90%88%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%A3%B0%E6%98%8E"><span class="toc-number">3.3.</span> <span class="toc-text"> 复合类型的声明</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E5%A4%9A%E4%B8%AA%E5%8F%98%E9%87%8F"><span class="toc-number">3.3.1.</span> <span class="toc-text"> 定义多个变量</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E5%90%91%E6%8C%87%E9%92%88%E7%9A%84%E6%8C%87%E9%92%88"><span class="toc-number">3.3.2.</span> <span class="toc-text"> 指向指针的指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E5%90%91%E6%8C%87%E9%92%88%E7%9A%84%E5%BC%95%E7%94%A8"><span class="toc-number">3.3.3.</span> <span class="toc-text"> 指向指针的引用</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#const-%E9%99%90%E5%AE%9A%E7%AC%A6"><span class="toc-number">4.</span> <span class="toc-text"> const 限定符</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#const"><span class="toc-number">4.1.</span> <span class="toc-text"> const</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96-%E5%92%8C-const"><span class="toc-number">4.1.1.</span> <span class="toc-text"> 初始化 和 const</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#const-%E7%9A%84%E5%BC%95%E7%94%A8"><span class="toc-number">4.2.</span> <span class="toc-text"> const 的引用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8C%87%E9%92%88-%E4%B8%8E-const"><span class="toc-number">4.3.</span> <span class="toc-text"> 指针 与 const</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E5%90%91%E5%B8%B8%E9%87%8F%E7%9A%84%E6%8C%87%E9%92%88"><span class="toc-number">4.3.1.</span> <span class="toc-text"> 指向常量的指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#const-%E6%8C%87%E9%92%88%E5%B8%B8%E9%87%8F%E6%8C%87%E9%92%88"><span class="toc-number">4.3.2.</span> <span class="toc-text"> const 指针(常量指针)</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%A1%B6%E5%B1%82-const-%E4%B8%8E-%E5%BA%95%E5%B1%82-const"><span class="toc-number">4.4.</span> <span class="toc-text"> 顶层 const 与 底层 const</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#constexpr-%E5%92%8C-%E5%B8%B8%E9%87%8F%E8%A1%A8%E8%BE%BE%E5%BC%8F"><span class="toc-number">4.5.</span> <span class="toc-text"> constexpr 和 常量表达式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#constexpr-%E5%8F%98%E9%87%8F"><span class="toc-number">4.5.1.</span> <span class="toc-text"> constexpr 变量</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E9%9D%A2%E5%80%BC%E7%B1%BB%E5%9E%8B"><span class="toc-number">4.5.2.</span> <span class="toc-text"> 字面值类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E9%92%88-%E5%92%8C-constexpr"><span class="toc-number">4.5.3.</span> <span class="toc-text"> 指针 和 constexpr</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%A4%84%E7%90%86%E7%B1%BB%E5%9E%8B"><span class="toc-number">5.</span> <span class="toc-text"> 处理类型</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%9E%8B%E5%88%AB%E5%90%8D"><span class="toc-number">5.1.</span> <span class="toc-text"> 类型别名</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8C%87%E9%92%88-%E5%B8%B8%E9%87%8F%E5%92%8C%E7%B1%BB%E5%9E%8B%E5%88%AB%E5%90%8D"><span class="toc-number">5.1.1.</span> <span class="toc-text"> 指针、常量和类型别名</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#auto-%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E%E7%AC%A6"><span class="toc-number">5.2.</span> <span class="toc-text"> auto 类型说明符</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%8D%E5%90%88%E7%B1%BB%E5%9E%8B-%E5%B8%B8%E9%87%8F-%E5%92%8C-auto"><span class="toc-number">5.2.1.</span> <span class="toc-text"> 复合类型、常量 和 auto</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#decltype-%E7%B1%BB%E5%9E%8B%E6%8C%87%E7%A4%BA%E7%AC%A6"><span class="toc-number">5.3.</span> <span class="toc-text"> decltype 类型指示符</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#decltype-%E5%92%8C-%E5%BC%95%E7%94%A8"><span class="toc-number">5.3.1.</span> <span class="toc-text"> decltype 和 引用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#auto-%E5%92%8C-decltype"><span class="toc-number">5.4.</span> <span class="toc-text"> auto 和 decltype</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84"><span class="toc-number">6.</span> <span class="toc-text"> 自定义数据结构</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89-sales_data-%E7%B1%BB%E5%9E%8B"><span class="toc-number">6.1.</span> <span class="toc-text"> 定义 Sales_data 类型</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E6%95%B0%E6%8D%AE%E6%88%90%E5%91%98"><span class="toc-number">6.2.</span> <span class="toc-text"> 类数据成员</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8-sales_data-%E7%B1%BB"><span class="toc-number">6.3.</span> <span class="toc-text"> 使用 Sales_data 类</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%96%E5%86%99%E8%87%AA%E5%B7%B1%E7%9A%84%E5%A4%B4%E6%96%87%E4%BB%B6"><span class="toc-number">6.4.</span> <span class="toc-text"> 编写自己的头文件</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8%E6%A6%82%E8%BF%B0"><span class="toc-number">6.4.1.</span> <span class="toc-text"> 预处理器概述</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%9C%AF%E8%AF%AD%E8%A1%A8"><span class="toc-number">7.</span> <span class="toc-text"> 术语表</span></a></li></ol></div><div class="related panel pjax" data-title="系列文章"><ul><li><a href="/introduce-cpp.html" rel="bookmark" title="初识 C++">初识 C++</a></li><li class="active"><a href="/cpp-variables.html" rel="bookmark" title="C++ 变量和基本类型">C++ 变量和基本类型</a></li><li><a href="/cpp-vectors.html" rel="bookmark" title="C++ 字符串、向量和数组">C++ 字符串、向量和数组</a></li><li><a href="/cpp-experssions.html" rel="bookmark" title="C++ 表达式">C++ 表达式</a></li><li><a href="/cpp-statement.html" rel="bookmark" title="C++ 语句">C++ 语句</a></li><li><a href="/cpp-functions.html" rel="bookmark" title="C++ 函数">C++ 函数</a></li><li><a href="/cpp-classes.html" rel="bookmark" title="C++ 类">C++ 类</a></li></ul></div><div class="overview panel" data-title="站点概览"><div class="author" itemprop="author" itemscope="itemscope" itemtype="http://schema.org/Person"><img class="image" loading="lazy" decoding="async" itemprop="image" alt="Jiankychen" src="/assets/avatar.webp"><p class="name" itemprop="name">Jiankychen</p><div class="description" itemprop="description"></div></div><nav class="state"><div class="item posts"><a href="/archives/"><span class="count">51</span><span class="name">文章</span></a></div><div class="item categories"><a href="/categories/"><span class="count">8</span><span class="name">分类</span></a></div><div class="item tags"><a href="/tags/"><span class="count">20</span><span class="name">标签</span></a></div></nav><div class="social"><a target="_blank" rel="noopener" href="https://github.com/jiankychen" class="item github" title="https://github.com/jiankychen"><i class="ic i-github"></i></a><a href="mailto:[email protected]" class="item email" title="mailto:[email protected]"><i class="ic i-envelope"></i></a><a target="_blank" rel="noopener" href="https://music.163.com/#/user/home?id=447771275" class="item music" title="https://music.163.com/#/user/home?id=447771275"><i class="ic i-cloud-music"></i></a><a target="_blank" rel="noopener" href="https://www.zhihu.com/people/jiankychen" class="item zhihu" title="https://www.zhihu.com/people/jiankychen"><i class="ic i-zhihu"></i></a></div><div class="menu"><li class="item"><a href="/" rel="section"><i class="ic i-home"></i>首页</a></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-feather"></i>文章</a><ul class="submenu"><li class="item"><a href="/archives/" rel="section"><i class="ic i-list-alt"></i>归档</a></li><li class="item"><a href="/categories/" rel="section"><i class="ic i-th"></i>分类</a></li><li class="item"><a href="/tags/" rel="section"><i class="ic i-tags"></i>标签</a></li></ul></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-feather"></i>链接</a><ul class="submenu"><li class="item"><a href="/peers/" rel="section"><i class="ic i-magic"></i>链环</a></li><li class="item"><a href="/friends/" rel="section"><i class="ic i-heart"></i>友链</a></li></ul></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-stars"></i>关于</a><ul class="submenu"><li class="item"><a href="/owner/" rel="section"><i class="ic i-user"></i>关于博主</a></li><li class="item"><a href="/site/" rel="section"><i class="ic i-paw"></i>关于本站</a></li><li class="item"><a href="/update/" rel="section"><i class="ic i-cloud"></i>更新日志</a></li></ul></li></div></div></div></div><ul id="quick"><li class="prev pjax"><a href="/cpp-vectors.html" rel="prev" title="上一篇"><i class="ic i-chevron-left"></i></a></li><li class="up"><i class="ic i-arrow-up"></i></li><li class="down"><i class="ic i-arrow-down"></i></li><li class="next pjax"><a href="/introduce-cpp.html" rel="next" title="下一篇"><i class="ic i-chevron-right"></i></a></li><li class="percent"></li></ul></div></div><div class="dimmer"></div></div></main><footer id="footer"><div class="inner"><div class="widgets"><div class="rpost pjax"><h2>随机文章</h2><ul><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-sort.html">LeetCode - 排序专题</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/C/" title="分类于C++">C++</a></div><span><a href="/cpp-vectors.html">C++ 字符串、向量和数组</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-stacks&queues.html">LeetCode - 栈与队列专题</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/hash-table.html">哈希表</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/C/" title="分类于C++">C++</a></div><span><a href="/cpp-statement.html">C++ 语句</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/binary-tree.html">二叉树</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/Dijkstra.html">Dijkstra</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/priority-queue.html">优先级队列</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Tutorial/" title="分类于Tutorial">Tutorial</a></div><span><a href="/vscode-intro.html">vscode 使用</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-string.html">LeetCode - 字符串专题</a></span></li></ul></div><div class="rpost pjax"><h2>最新评论</h2><ul class="leancloud-recent-comment" id="new-comment"></ul></div></div><div class="status"><div class="copyright">© 2021 -<span itemprop="copyrightYear">2024</span><span class="with-love"><i class="ic i-sakura rotate"></i></span><span class="author" itemprop="copyrightHolder">Jiankychen @ Jiankychen</span></div><div class="count"><span class="post-meta-item-icon"><i class="ic i-chart-area"></i></span><span title="站点总字数">955k 字</span><span class="post-meta-divider"> | </span><span class="post-meta-item-icon"><i class="ic i-coffee"></i></span><span title="站点阅读时长">14:28</span></div><div class="powered-by">基于 <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a> & Theme.<a target="_blank" rel="noopener" href="https://github.com/theme-shoka-x/hexo-theme-shokaX/">ShokaX</a></div></div><script src="https://unpkg.com/[email protected]/bsz.pure.mini.js"></script><div id="busuanzi-wrap"><span class="ic i-eye"></span><span id="busuanzi_container_site_pv">本站总访问量 <span id="busuanzi_value_site_pv"></span> 次</span> | <span class="ic i-user"></span><span id="busuanzi_container_site_uv">本站总访客量 <span id="busuanzi_value_site_uv"></span> 次</span></div></div></footer></div><script data-config="" type="text/javascript">var LOCAL = {
ispost: true,
path: `/cpp-variables`,
favicon: {
show: `Jiankychen`,
hide: `Jiankychen`
},
search: {
placeholder: "文章搜索",
empty: "关于 「 ${query} 」,什么也没搜到",
stats: "${time} ms 内找到 ${hits} 条结果"
},
copy_tex: true,
katex: true,
mermaid: false,
audio: undefined,
fancybox: true,
nocopy: false,
outime: true,
template: `<div class="note warning"><p><span class="label warning">文章时效性提示</span><br>这是一篇发布于 {{publish}} 天前,最后一次更新在 {{updated}} 天前的文章,部分信息可能已经发生改变,请注意甄别。</p></div>`,
quiz: {
choice: `单选题`,
multiple: `多选题`,
true_false: `判断题`,
essay: `问答题`,
gap_fill: `填空题`,
mistake: `错题备注`
},
ignores: [
(uri) => uri.includes('#'),