『Cプログラミング専門課程』目次第4章メモリ

4.6 const修飾子 (1)


ANSI C には「const修飾子」が導入されています。ANSI C 準拠でないCで も多くのCに導入されていて、未導入のCコンパイラは少ないでしょう。これ は要するに、定数だ、変化はしない、と宣言するものです。const修飾子はコ ンパイルの最適化を促すだけではなく、プログラムの安全性の向上、バグの撃 退に非常に効果があります。実際には最適化の効果は微々たるもので、主眼は 安全性、信頼性の向上です。

しかし、const修飾子についての解説はどのCの本を見てもわずかで、軽視 も甚だしい。少しでもプログラムの信頼性向上を図ろうとすれば非常に重要な 助っ人であり、これを無視するのは、わざわざ信頼性向上のためにC言語に導 入された機能を生かさないことになります。そういう理由で、本書ではconst を特に詳しく解説します。

次は、文字列の一部を変更するプログラムです。どういう動作をするか考え てください。

サンプルプログラム

   1: /*  strlit.c  --  文字列リテラル  */
   2: 
   3: #include        
   4: 
   5:         char    p[] = "abc";
   6:         char    *q  = "def";
   7: 
   8: main()
   9: {
  10:         printf( "p : %p\n", p );
  11:         printf( "p : %s\n", p );
  12:         p[1] = '@';
  13:         printf( "p : %s\n", p );
  14: 
  15:         printf( "&q: %p\n", &q );
  16:         printf( "q : %p\n", q );
  17:         printf( "q : %s\n", q );
  18:         q[1] = '@';
  19:         printf( "q : %s\n", q );
  20: }

変数 p と q の違いが分かりましたか。pはヌル文字も含めてサイズが4の配 列です。文字列"abc"は、全て配列pの中にあり、メモリ上のその他のいずれの 場所にも存在しません。

変数qは、文字へのポインタです。"def"のように "(2重引用符)で囲んだ 文字列を「文字列リテラル」と呼びます。文字列リテラルがchar型配列の初期 値として与えられたときは、その配列に値(文字)がセットされますが、char 型へのポインタの初期値として与えられたとき、どういう動作をするでしょう か。

配列pとポインタqに対して、代入

  12:         p[1] = '@';
  18:         q[1] = '@';
  
をしていますが、実行結果はどうなったでしょうか。

実行結果

p : 40b0
p : abc
p : a@c
&q: 40b4
q : 2290
q : def
Segmentation fault (core dumped)

最後のprintf文の実行の前に、

Segmentation fault (core dumped)
  

が表示されています。この部分の動作はコンピュータやコンパイラによって異 なります。"Segmentation fault (core dumped)" というメッセージは、コン ピュータにできるはずのない無茶苦茶な要求をしたため、core(コア)を吐い たのです。無理矢理酒を飲ませて、ゲロを吐いたようなものです。

状況からいって、

  18:         q[1] = '@';
  

に原因があることは察せられるでしょう。ポインタqが指している先は、文字 列リテラルです。

文字列リテラルは無条件にconst修飾子がついているものと見なされます。 したがって、文字列リテラルの実体は無条件に「静的記憶領域」に置かれます。 ここで使用しているコンピュータでは、静的記憶領域を、変更可能な領域と、 変更不可能な領域に分けていて、静的でconstなものは呼び出し専用メモリ (ROM)領域に置かれます。さらに、このコンピュータでは、ROM領域は自由に 読み出せますが、書き込もうとすると無視するのではなく、コアを吐き、実行 を強制的に中断します。

アドレスは、変数pが40b0、変数qが40b4で隣り合っていますが、 qの値、つ まり実際に文字列リテラルが確保されているアドレスは2290で、少々離れてい ます。

文字列リテラルはstaticが暗黙の内についていて、必ず静的記憶領域に確保 されます。だから、たとえ関数中で使われても、文字列リテラルの実体は静的 記憶領域に確保されます。

⇒⇒⇒⇒次ページへ


Copyright1996 Hirofumi Fujiwara. No reproduction or republication without written permission
『Cプログラミング専門課程』目次第4章メモリ