Skip to content

Commit 949ee1d

Browse files
committed
Updated to Chapter 6, Section 4
1 parent cc092d1 commit 949ee1d

File tree

11 files changed

+195
-58
lines changed

11 files changed

+195
-58
lines changed

generalized_parts/05_composite_types_and_their_use/05_string_with_arrays.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ \subsection*{字符串是如何构成的?}
7575
\includegraphics[width=\textwidth]{../images/generalized_parts/05_Information_in_the_string_300.png}
7676
\caption{字符串 \lstinline@name@ 中信息的变化情况}
7777
\end{figure}
78-
那么接下来我通过 \lstinline@strcpy@ 把另一个名字复制到 \lstinline@name@ 中,这样做会修改原来字符串的信息。
78+
那么接下来我通过 \lstinline@strcpy@\footnote{MSVC会默认阻止我们使用 \lstinline@strcpy@ 和 \lstinline@strncpy@ 函数。为了解决这个问题,我们可以更改设置,或直接在源代码的最前面加一行 \texttt{\#define \_CRT\_SECURE\_NO\_WARNINGS 1}。}把另一个名字复制到 \lstinline@name@ 中,这样做会修改原来字符串的信息。
7979
\begin{lstlisting}
8080
strcpy(name, "Donald Ervin Knuth");
8181
//strcpy会把第二个参数中的有效部分复制到第一个参数起的内存空间中

generalized_parts/06_custom_types_and_their_use/04_union.tex

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,80 @@ \section{联合体}
3939
0x7ffc00a4d280\\
4040
0x7ffc00a4d280
4141
}\\\noindent\rule{\linewidth}{.2pt}
42-
这说明联合体的所有成员都在同一个位置存着呢。
42+
这说明联合体的所有成员都在同一个位置存着呢。\par
43+
\subsection*{联合体如何组织内存?}
44+
正如我们才看到的那样,联合体会把所有的成员放在同一个内存区域中。所以 \lstinline@ValType@ 的内存占用不需要达到全部成员的内存占用之和,只需要等于它们之中最大的那个就行。在这里,如果你的开发环境中 \lstinline@long double@ 类型占用8字节,那么 \lstinline@sizeof ValType@ 的结果一般就是8字节;如果你的开发环境中 \lstinline@long double@ 类型占用16字节,那么 \lstinline@sizeof ValType@ 的结果一般就是16字节。\par
45+
既然五个成员全都占用同样的内存空间,那么它们之间势必会存在冲突。回想一下章首的图6.1——我们用不同类型去解释同一块内存空间中的内容,将会得到不同的结果。在这里也是如此。如果我们用 \lstinline@a.val_i@ 去修改这段内存中的内容,那么它将会按照 \lstinline@int@ 型对信息的组织方式来存储 \lstinline@0@/\lstinline@1@ 串。在这种情况下,输出 \lstinline@a.val_c@ 或 \lstinline@a.val_d@ 往往是看不出什么有效内容的。\par
46+
既然这些成员之间互相有冲突,那么我们为什么还要用联合体呢?这是因为,在有些情况下,一个联合体的成员是彼此互斥的,它们不会同时出现。还是以 \lstinline@ValType@ 为例,如果用一个结构体来表达它的话,需要29或更多字节才能容纳——其中有用的信息永远不超过16字节,那么剩下的空间就浪费了。通过联合体,我们可以把这些数据都存到这8或16个字节的空间当中,只要它们不同时出现,那就不会存在冲突了。
47+
举个现实一点的例子:像Python这样的编程语言支持动态类型,即,一个变量可以在某个时候是整型,而在之后变成字符型或浮点型。而C++是静态类型语言,一个变量的类型在它定义的时候就确定下来了,不能更改。通过联合体,我们可以在一定程度上实现动态类型的部分功能。\par
48+
\begin{lstlisting}
49+
struct dynamic { //自定义动态类型
50+
enum Types{integer,floating,string}; //枚举,用来规定可能的类型
51+
Types type; //type用来标记当前的类型
52+
union Value { //联合体,它的三个成员不会同时出现
53+
long long vll; //整型
54+
long double vld; //浮点型
55+
char str[16]; //字符串型
56+
};
57+
Value value; //定义Value类的对象value
58+
};
59+
\end{lstlisting}\par
60+
我来解释一下这段代码:我们定义了一个 \lstinline@dynamic@ 类,这个类的对象可能以三种状态存在:整型状态,浮点型状态或字符型状态。\lstinline@type@ 是一个枚举类型,它可以用来标记这个对象当前处于何种状态。\par
61+
而在联合体 \lstinline@Value@ 中,三个成员 \lstinline@vll@, \lstinline@vld@ 和 \lstinline@str@ 分别是整型、浮点型和字符串型。在任何时刻,这个变量只能是这三种类型之一,所以它们不会同时出现,用 \lstinline@union@ 就很合理。\par
62+
\subsection*{如何使用联合体?}
63+
那么我们就在主函数中定义一个 \lstinline@dynamic@ 类的对象,并设置它的类型和值。
64+
\begin{lstlisting}
65+
dynamic number {dynamic::integer}; //integer在dynamic域中,所以用dynamic::
66+
number.value.vll = 15; //修改dynamic.value的vll成员,把它变成15
67+
\end{lstlisting}
68+
这里需要注意,\lstinline@integer@ 是 \lstinline@dynamic@ 域中的枚举项。如果我们要在域外使用,就必须用 \lstinline@dynamic::integer@。相关细节,我们留到第七章再谈。\par
69+
在本段代码中,我们定义了一个 \lstinline@number@ 变量,并初始化它的 \lstinline@type@ 成员为 \lstinline@integer@。接下来,我们用 \lstinline@number.value.vll@ 来为 \lstinline@value.vll@ 成员赋值。一旦为 \lstinline@vll@ 赋值,这时 \lstinline@vll@ 就是活跃成员,而 \lstinline@vld@ 和 \lstinline@str@ 都是不活跃成员。试图访问不活跃成员是未定义行为,会得到不确定的结果\footnote{约等于定义了局部变量但还没有赋值/初始化就开始使用它,会得到不确定的结果。}。\par
70+
下一刻,我们想把 \lstinline@number@ 改成浮点型。这个操作非常简单,只要为 \lstinline@vld@ 赋值,就可以把它变成活跃成员,顶掉 \lstinline@vll@。\par
71+
\begin{lstlisting}
72+
number.type = dynamic::floating; //修改number的类型,标记为浮点数
73+
number.value.vld = number.value.vll; //vld将取代vll成为活跃成员
74+
cout << number.value.vld; //会输出15
75+
\end{lstlisting}
76+
第一行的操作就是更改一下标签,不必多说。第二行的操作却有些费解——不是说同一个联合体的不同成员不该同时出现吗?为什么我们在这里可以用 \lstinline@vll@ 给 \lstinline@vld@ 赋值还不产生问题呢?\par
77+
其实这就是``同时''的问题了。赋值运算符不是一下子就把右操作数的值赋给左值的。赋值操作的内部过程可以分成两步:第一步是处理右操作数,把右操作数的值转移给一个临时变量\footnote{这是一个左值到右值的转换,可能还会伴随类型转换。};第二步是把这个临时变量的值转移给一左操作数,临时变量销毁。我们看,在第一步的时候 \lstinline@vll@ 是活跃成员,这时我们没有用到 \lstinline@vld@,没有问题;而在第二步的时候 \lstinline@vld@ 是活跃成员,这时我们没有用到 \lstinline@vll@,也没有问题,如图6.10所示。\par
78+
\begin{figure}[htbp]
79+
\centering
80+
\includegraphics[width=0.8\textwidth]{../images/generalized_parts/06_process_of_assignment_to_union_300.png}
81+
\caption{赋值过程中,活跃成员的变化}
82+
\end{figure}
83+
试过了这些简单功能之后,我们可以写一些函数来实现它们。以下是一个输出函数,它根据 \lstinline@type@ 来判断要输出哪个成员。
84+
\begin{lstlisting}
85+
void output(const dynamic &number, ostream &out = {cout}) {
86+
switch (number.type) { //用switch来判断number.type的值
87+
case dynamic::integer: //如果是整型
88+
out << number.value.vll; //输出vll
89+
return; //直接用return返回;或者用break也行
90+
case dynamic::floating: //如果是浮点型
91+
out << number.value.vld; //输出vld
92+
return;
93+
case dynamic::string: //如果是字符串
94+
out << number.value.str; //输出str
95+
return;
96+
}
97+
}
98+
\end{lstlisting}
99+
这个函数仍然沿袭我们的思路,以 \lstinline@cout@ 作为输出的默认参数。在函数中,我们用 \lstinline@switch@-\lstinline@case@ 结构来实现对 \lstinline@number.type@ 的判断。这个函数没有什么技术含量,读者想必很容易就能看懂。\par
100+
\begin{lstlisting}
101+
dynamic& assign_int(dynamic &number, long long ll) {
102+
number.type = dynamic::integer; //更改type
103+
number.value.vll = ll; //赋值给vll,现在它是活跃成员
104+
return number;
105+
}
106+
dynamic& assign_float(dynamic &number, long double ld) {
107+
number.type = dynamic::floating; //更改type
108+
number.value.vld = ld; //赋值给vld,现在它是活跃成员
109+
return number;
110+
}
111+
dynamic& assign_str(dynamic &number, const char *src) {
112+
number.type = dynamic::floating; //更改type
113+
strncpy(number.value.str, src, 16); //用strncpy为str赋值,现在它是活跃成员
114+
return number;
115+
}
116+
\end{lstlisting}
117+
在这里,我们定义了三个函数,分别用于给 \lstinline@number@ 赋特定类型的值。至于字符串的赋值,我们提过,字符串不能直接赋值,要用 \lstinline@strcpy@ 或 \lstinline@strncpy@ 才行。这两个函数在头文件 \lstinline@cstring@ 中。\lstinline@strncpy@ 比 \lstinline@strcpy@ 更安全,它可以规定第一个操作数最多能接收的字符数量,以防字符串赋值时发生越界问题。\par
118+
我们还可以借助联合体玩更多花样。不过相比于 \lstinline@class@/\lstinline@struct@ 来说,它的应用范围还是很窄的。接下来的章节中我们几乎不会再用 \lstinline@union@ 了,所以这里就仅为读者开拓一下眼界。想要了解关于联合体的更多用法,可以参考 \href{https://stackoverflow.com/questions/4788965/when-would-anyone-use-a-union-is-it-a-remnant-from-the-c-only-days}{When would anyone use a union? Is it a remnant from the C-only days?-Stack Overflow}。\par
31.3 KB
Loading
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<mxfile host="Electron" modified="2024-01-06T12:36:43.882Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.16 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="M2DLy-5ZFWzBB2YG3y62" version="22.1.16" type="device">
2+
<diagram name="Page-1" id="ry_gHJNBP8D9PPL1evwy">
3+
<mxGraphModel dx="558" dy="383" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
4+
<root>
5+
<mxCell id="0" />
6+
<mxCell id="1" parent="0" />
7+
<mxCell id="LePHzGJDI9glpyWeBq7U-2" value="&lt;font face=&quot;consolas&quot;&gt;value.vll&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" vertex="1" parent="1">
8+
<mxGeometry x="200" y="230" width="90" height="30" as="geometry" />
9+
</mxCell>
10+
<mxCell id="LePHzGJDI9glpyWeBq7U-6" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="LePHzGJDI9glpyWeBq7U-3" target="LePHzGJDI9glpyWeBq7U-5">
11+
<mxGeometry relative="1" as="geometry" />
12+
</mxCell>
13+
<mxCell id="LePHzGJDI9glpyWeBq7U-14" value="赋值第一步" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="LePHzGJDI9glpyWeBq7U-6">
14+
<mxGeometry x="0.2" y="-1" relative="1" as="geometry">
15+
<mxPoint x="-6" y="-16" as="offset" />
16+
</mxGeometry>
17+
</mxCell>
18+
<mxCell id="LePHzGJDI9glpyWeBq7U-3" value="&lt;font face=&quot;consolas&quot;&gt;value.vld&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#EEEEEE;gradientColor=none;strokeColor=#36393d;" vertex="1" parent="1">
19+
<mxGeometry x="200" y="260" width="90" height="30" as="geometry" />
20+
</mxCell>
21+
<mxCell id="LePHzGJDI9glpyWeBq7U-4" value="&lt;font face=&quot;consolas&quot;&gt;value.str&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#EEEEEE;gradientColor=none;strokeColor=#36393d;" vertex="1" parent="1">
22+
<mxGeometry x="200" y="290" width="90" height="30" as="geometry" />
23+
</mxCell>
24+
<mxCell id="LePHzGJDI9glpyWeBq7U-13" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="LePHzGJDI9glpyWeBq7U-5" target="LePHzGJDI9glpyWeBq7U-9">
25+
<mxGeometry relative="1" as="geometry" />
26+
</mxCell>
27+
<mxCell id="LePHzGJDI9glpyWeBq7U-15" value="赋值第二步" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="LePHzGJDI9glpyWeBq7U-13">
28+
<mxGeometry x="0.019" y="1" relative="1" as="geometry">
29+
<mxPoint y="-14" as="offset" />
30+
</mxGeometry>
31+
</mxCell>
32+
<mxCell id="LePHzGJDI9glpyWeBq7U-7" value="&lt;font face=&quot;consolas&quot;&gt;value.vll&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" vertex="1" parent="1">
33+
<mxGeometry x="350" y="230" width="90" height="30" as="geometry" />
34+
</mxCell>
35+
<mxCell id="LePHzGJDI9glpyWeBq7U-5" value="&lt;font face=&quot;consolas&quot;&gt;value.vld&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#EEEEEE;gradientColor=none;strokeColor=#36393d;" vertex="1" parent="1">
36+
<mxGeometry x="350" y="260" width="90" height="30" as="geometry" />
37+
</mxCell>
38+
<mxCell id="LePHzGJDI9glpyWeBq7U-8" value="&lt;font face=&quot;consolas&quot;&gt;value.str&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#EEEEEE;gradientColor=none;strokeColor=#36393d;" vertex="1" parent="1">
39+
<mxGeometry x="350" y="290" width="90" height="30" as="geometry" />
40+
</mxCell>
41+
<mxCell id="LePHzGJDI9glpyWeBq7U-10" value="&lt;font face=&quot;consolas&quot;&gt;value.vll&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#eeeeee;strokeColor=#36393d;" vertex="1" parent="1">
42+
<mxGeometry x="500" y="230" width="90" height="30" as="geometry" />
43+
</mxCell>
44+
<mxCell id="LePHzGJDI9glpyWeBq7U-9" value="&lt;font face=&quot;consolas&quot;&gt;value.vld&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" vertex="1" parent="1">
45+
<mxGeometry x="500" y="260" width="90" height="30" as="geometry" />
46+
</mxCell>
47+
<mxCell id="LePHzGJDI9glpyWeBq7U-11" value="&lt;font face=&quot;consolas&quot;&gt;value.str&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#EEEEEE;gradientColor=none;strokeColor=#36393d;" vertex="1" parent="1">
48+
<mxGeometry x="500" y="290" width="90" height="30" as="geometry" />
49+
</mxCell>
50+
</root>
51+
</mxGraphModel>
52+
</diagram>
53+
</mxfile>

0 commit comments

Comments
 (0)