📄 chapter08.html
字号:
because the compiler knows <B>j</B> is <B>const</B> and that the value is valid
even if storage was allocated to hold that value at some point in the
program.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main( )</B>, you see a
different kind of <B>const</B> in the identifier <B>c</B> because the value
cannot be known at compile time. This means storage is required, and the
compiler doesn’t attempt to keep anything in its symbol table (the same
behavior as in C). The initialization must still happen at the point of
definition, and once the initialization occurs, the value cannot be changed. You
can see that <B>c2</B> is calculated from <B>c</B> and also that scoping works
for <B>const</B>s<A NAME="Index1468"></A> as it does for any other type –
yet another improvement over the use of <B>#define</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As a matter of practice, if you think a
value shouldn’t change, you should make it a <B>const</B>. This not only
provides insurance against inadvertent changes, it also allows the compiler to
generate more efficient code by eliminating storage and memory reads.
</FONT><A NAME="_Toc312373906"></A><A NAME="_Toc472654880"></A><BR></P></DIV>
<A NAME="Heading251"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Aggregates</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s possible to use <B>const</B>
for aggregates, <A NAME="Index1469"></A><A NAME="Index1470"></A>but you’re
virtually assured that the compiler will not be sophisticated enough to keep an
aggregate in its symbol table, so storage will be allocated. In these
situations, <B>const</B> means “a piece of storage that cannot be
changed.” However, the value cannot be used at compile time because the
compiler is not required to know the contents of the storage at compile time. In
the following code, you can see the statements that are
illegal:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C08:Constag.cpp</font>
<font color=#009900>// Constants and aggregates</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font> i[] = { 1, 2, 3, 4 };
<font color=#009900>//! float f[i[3]]; // Illegal</font>
<font color=#0000ff>struct</font> S { <font color=#0000ff>int</font> i, j; };
<font color=#0000ff>const</font> S s[] = { { 1, 2 }, { 3, 4 } };
<font color=#009900>//! double d[s[1].j]; // Illegal</font>
<font color=#0000ff>int</font> main() {} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In an
<A NAME="Index1471"></A><A NAME="Index1472"></A>array definition, the compiler
must be able to generate code that moves the stack pointer to accommodate the
array. In both of the illegal definitions above, the compiler complains because
it cannot find a constant expression in the array
definition.</FONT><A NAME="_Toc312373907"></A><A NAME="_Toc472654881"></A><BR></P></DIV>
<A NAME="Heading252"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Differences with C</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Constants were introduced in early
versions of C++ while the Standard C specification was still being finished.
Although the C committee then decided to include <B>const</B> in C, somehow
it<B> <A NAME="Index1473"></A><A NAME="Index1474"></A></B>came to mean for them
“an ordinary variable that cannot be changed.” In C, a <B>const</B>
always occupies storage and its name is global. The C compiler cannot treat a
<B>const </B>as a compile-time constant. In C, if you say</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize = 100;
<font color=#0000ff>char</font> buf[bufsize];</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">you will get an error, even though it
seems like a rational thing to do. Because <B>bufsize</B> occupies storage
somewhere, the C compiler cannot know the value at compile time. You can
optionally say</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">in C, but not in C++, and the C compiler
accepts it as a declaration indicating there is storage allocated elsewhere.
Because <A NAME="Index1475"></A>C defaults to external linkage
<A NAME="Index1476"></A><A NAME="Index1477"></A>for <B>const</B>s, this makes
sense. C++ defaults to internal linkage
<A NAME="Index1478"></A><A NAME="Index1479"></A>for <B>const</B>s so if you want
to accomplish the same thing in C++, you must explicitly change the linkage to
external using <B>extern</B>:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>extern</font> <font color=#0000ff>const</font> <font color=#0000ff>int</font> bufsize; <font color=#009900>// Declaration only</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This line also works in
C.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C++, a <B>const</B> doesn’t
necessarily create storage. In C a <B>const</B> always creates
<A NAME="Index1480"></A>storage. Whether or not storage is reserved for a
<B>const</B> in C++ depends on how it is used. In general, if a <B>const</B> is
used simply to replace a name with a value (just as you would use a
<B>#define</B>), then storage doesn’t have to be created for the
<B>const</B>. If no storage is created (this depends on the complexity of the
data type and the sophistication of the compiler), the values may be folded into
the code for greater efficiency after type checking, not before, as with
<B>#define</B>. If, however, you take an address of a
<A NAME="Index1481"></A><B>const<A NAME="Index1482"></A></B> (even unknowingly,
by passing it to a function that takes a reference argument) or you define it as
<B>extern</B>, then storage is created for the <B>const</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C++, a <B>const</B> that is outside
all functions has file scope<A NAME="Index1483"></A><A NAME="Index1484"></A>
(i.e., it is invisible outside the file). That is, it defaults to internal
linkage. This is very different from all other identifiers in C++ (and from
<B>const</B> in C!) that default to external linkage. Thus, if you declare a
<B>const</B> of the same name in two different files and you don’t take
the address or define that name as <B>extern</B>, the ideal C++ compiler
won’t allocate storage for the <B>const</B>, but simply fold it into the
code<A NAME="Index1485"></A>. Because <B>const</B> has implied file scope, you
can put it in C++ header files with no conflicts at link time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since a <B>const</B> in C++ defaults to
internal linkage<A NAME="Index1486"></A><A NAME="Index1487"></A>, you
can’t just define a <B>const</B> in one file and reference it as an
<B>extern</B> in another file. To give a <B>const</B> external
linkage<A NAME="Index1488"></A><A NAME="Index1489"></A> so it can be referenced
from another file, you must explicitly define it as
<A NAME="Index1490"></A><B>extern<A NAME="Index1491"></A><A NAME="Index1492"></A></B>,
like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>extern</font> <font color=#0000ff>const</font> <font color=#0000ff>int</font> x = 1;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that by giving it an initializer
and saying it is <B>extern</B>, you force storage to be created for the
<B>const</B> (although the compiler still has the option of doing constant
folding here). The initialization establishes this as a definition, not a
declaration. The declaration:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>extern</font> <font color=#0000ff>const</font> <font color=#0000ff>int</font> x;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">in C++ means that the definition exists
elsewhere (again, this is not necessarily true in C). You can now see why C++
requires a <B>const</B> definition to have an initializer: the initializer
distinguishes a <A NAME="Index1493"></A>declaration from a
<A NAME="Index1494"></A>definition (in C it’s always a definition, so no
initializer is necessary). With an <A NAME="Index1495"></A><B>extern</B>
<B>const</B> declaration, the compiler cannot do constant folding because it
doesn’t know the value.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The C approach to <B>const</B> is not
very useful, and if you want to use a named value inside a constant expression
(one that must be evaluated at compile time), <A NAME="Index1496"></A>C almost
<I>forces </I>you to use <B>#define</B> in the
preprocessor.</FONT><A NAME="_Toc305628676"></A><A NAME="_Toc312373908"></A><A NAME="_Toc472654882"></A><BR></P></DIV>
<A NAME="Heading253"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Pointers</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Pointers can be made <B>const</B>. The
compiler will still endeavor to prevent storage allocation and do constant
folding when dealing with <B>const</B>
pointers<A NAME="Index1497"></A><A NAME="Index1498"></A>, but these features
seem less useful in this case. More importantly, the compiler will tell you if
you attempt to change a <B>const </B>pointer, which adds a great deal of
safety.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When using <B>const</B> with pointers,
you have two options: <B>const</B> can be applied to what the pointer is
pointing to, or the <B>const</B> can be applied to the address stored in the
pointer itself. The syntax for these is a little confusing at first but becomes
comfortable with
practice.</FONT><A NAME="_Toc312373909"></A><A NAME="_Toc472654883"></A><BR></P></DIV>
<A NAME="Heading254"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Pointer to const</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The trick with a pointer definition, as
with any complicated definition, is to read it starting at the identifier and
work your way out. The <B>const</B> specifier binds to the thing it is
“closest to.” So if you want to prevent any changes to the element
you are pointing to, you write a definition like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>const</font> <font color=#0000ff>int</font>* u;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Starting from the identifier, we read
“<B>u</B> is a pointer, which points to a <B>const</B> <B>int</B>.”
Here, no initialization is required because you’re saying that <B>u</B>
can point to anything (that is, it is not <B>const</B>), but the thing it points
to cannot be changed.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the mildly confusing part.
You might think that to make the pointer itself unchangeable, that is, to
prevent any change to the address contained inside <B>u</B>, you would simply
move the <B>const</B> to the other side of the <B>int</B> like
this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> <font color=#0000ff>const</font>* v;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s not all that crazy to think
that this should read “<B>v</B> is a <B>const</B> pointer to an
<B>int</B>.” However, the way it <I>actually</I> reads is “<B>v</B>
is an ordinary pointer to an <B>int</B> that happens to be <B>const</B>.”
That is, the <B>const</B> has bound itself to the <B>int</B> again, and the
effect is the same as the previous definition. The fact that these two
definitions are the same is the confusing point; to prevent this confusion on
the part of your reader, you should probably stick to the first
form.</FONT><A NAME="_Toc312373910"></A><A NAME="_Toc472654884"></A><BR></P></DIV>
<A NAME="Heading255"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
const pointer</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To make the pointer itself a
<B>const</B>, you must place the <B>const</B> specifier to the right of the
<B>*</B>, like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> d = 1;
<font color=#0000ff>int</font>* <font color=#0000ff>const</font> w = &d;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><I>Now</I> it reads: “<B>w</B> is a
pointer, which is <B>const</B>, that points to an <B>int</B>.” Because the
pointer itself is now the <B>const</B>, the compiler requires that it be given
an initial value that will be unchanged for the life of that pointer. It’s
OK, however, to change what that value points to by saying </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>*w = 2;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can also make a <B>const</B> pointer
to a <B>const</B> object using either of two legal forms:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> d = 1;
<font color=#0000ff>const</font> <font color=#0000ff>int</font>* <font color=#0000ff>const</font> x = &d; <font color=#009900>// (1)</font>
<font color=#0000ff>int</font> <font color=#0000ff>const</font>* <font color=#0000ff>const</font> x2 = &d; <font color=#009900>// (2)</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now neither the pointer nor the object
can be changed.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Some people argue that the second form is
more consistent because the <B>const</B> is always placed to the right of what
it modifies. You’ll have to decide which is clearer for your particular
coding style.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here are the above lines in a compileable
file:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C08:ConstPointers.cpp</font>
<font color=#0000ff>const</font> <font color=#0000ff>int</font>* u;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -