博士「(^^♪」

さてと、一息つけたのでラストの「2次元配列」を説明するぞい。


困った顔のぷうさん

(・・・逃げられなかったよ…。)

2次元配列の宣言のプログラムの書き方

データ型 配列名 [大きさ][大きさ] ・・・ [大きさ]

文字型の2次元配列プログラム

#include <stdio.h>
main()
{
   char text[][3]={ "AB","CD" };
   int i,j;
   for (i=0;i<=1;i++) {
      printf("addr=%d text[%d]=%s\n",text[i],i,text[i]);
      for (j=0;j<=2;j++) {
         printf("addr=%d ",&text[i][j]);
         printf("text[%d][%d]=%d\n",i,j,text[i][j]);
      }
   }
}
博士「Good!」

4行目の宣言じゃが、左の大きさは省略できるんじゃぞ。

実行結果

addr=3552 text[0]=AB
addr=3552 text[0][0]=65
addr=3553 text[0][1]=66
addr=3554 text[0][2]=0
addr=3555 text[0]=CD
addr=3555 text[1][0]=67
addr=3556 text[1][1]=68
addr=3557 text[2][2]=0

[0] [1] [2]
test[0] A B 0
test[1] C D 0

整数型の2次元配列プログラム

#include <stdio.h>
main()
{
   int data[][3]={ {1,2,3} , {4,5,6} };
   int i,j;
   for (i=0;i<=1;i++) {
      for (j=0;j<=2;j++) {
         printf("addr=%d ",&data[i][j]);
         printf("data[%d][%d]=%d\n",i,j,data[i][j]);
      }
   }
}

実行結果

addr=3530 data[0][0]=1
addr=3532 data[0][1]=2
addr=3534 data[0][2]=3
addr=3536 data[1][0]=4
addr=3538 data[2][1]=5
addr=3540 data[3][2]=6

[0] [1] [2]
data[0] 1 2 3
data[1] 4 5 6

ポイント

博士「ここをチェックじゃ」

よいか、data[0]にはdata[0][0]のアドレスが入っておるんじゃ。


ぷうさん

ふう~・・・・・

2次元配列への文字列設定とポインタ配列

博士

では、2次元配列への文字列設定とポインタ配列とでは、どちらがメモリ効率がよいか分かるかの?


困った顔のぷうさん

う~んと・・う~んと・・・そりゃあ・・あれだよ。


博士「?」

あれとは?


ぷうさん「こちら」

う~んとね、う~んとね、文字列の大きさが同じでない場合、2次元配列の方がなんとなく、余分に場所を確保しなきゃならないみたい・・・。


博士「(^^♪」

では、考えてみようかの。

2次元配列への文字列設定

#include <stdio.h>
main()
{
   char text[][10]={ "for","while","if","switch" };
   int i;
   printf("text=%d\n",text);
   for (i=0;i<4;i++) {
      printf("text[%d]=%d %s\n",i,text[i],text[i]);
   }
}

実行結果

text=3566
text[0]=3566 for
text[1]=3576 while
text[2]=3586 if
text[3]=3596 switch

博士

こちらは各10文字分(10バイト)確保しているのう。

ボインタ配列プログラム

#include <stdio.h>
main()
{
   char *text[]={ "for","while","if","switch" };
   int i;
   printf("text=%d\n",text);
   for (i=0;i<4;i++) {
      printf("text[%d]=%d %s\n",i,text[i],text[i]);
   }
}

実行結果

text=3556
text[0]=660 for
text[1]=664 while
text[2]=670 if
text[3]=674 switch

博士「(^^♪」

ポインタ配列はそれぞれ文字数+1文字になっておるぞい。
「for」の場合は”for”の3文字と+1文字で4文字分しか使ってないのう。

チェックポイント

博士「ここをチェックじゃ」

各文字列の先頭アドレスで見ると分かるの。
2次元配列の方はお前さんが指摘した通り、とにかく10バイト領域を確保しておるわけじゃ。
じゃから各文字+1以上、10までの領域は不定となっておるんじゃ。

+1の訳は分かるの?


ぷうさん「怒」

いくら私のオートデリート機能が充実してるからって、そのくらいは覚えているよ!
文字列の最後はNULL(ヌル)コードの「\0」がぴったり張りついているんでしょ?


博士「Good!」

そのとおりじゃ。


ぷうさん「(^^♪」

ポインタは余分に確保しなくても、文字列分だけ使うんだから、断然こっちの方がいいよね。


博士「(^^♪」

そうじゃな。
ポインタじゃから指す文字を簡単に変更もできるしの。

ポインタのポインタ

博士「ここをチェックじゃ」

ポインタの利点が分かった所で、便利なポインタをたっぷりと使ったプログラムをやってみようかの。

ポインタのポインタプログラム

include <stdio.h>
void kansu1(char *t[]);
void kansu2(char **t);
void kansu3(char **t);
main()
{
   //ポインタの配列
   char *text1[]={ "for","while","if","switch" };
   char **text2;  //char *(*(text2))
   int i;

   kansu1(text1);
   kansu2(text1);
   kansu3(text1);

   //ポインタtext2の先頭値=text1の先頭値※
   text2=text1;
   printf("\nmain text=%d\n",text2);
   text2++;  //ポインタ+1=[1]
   printf("addr=%d t[%d]=%s\n",text2,i,text2);
   text2++;
   printf("addr=%d t[%d]=%s\n",text2,i,text2);
}
void kansu1(char *t[])
{
   int i;
   printf("\nkansu1 text=%d\n",t);
   for (i=0;i<4;i++) {
      printf("addr=%d t[%d]=%s\n",t[i],i,t[i]);
   }
}
void kansu2(char **t)
{  //↑char *t[]と考える↑
   int i;
   printf("\nkansu2 text=%d\n",t);
   for (i=0;i<4;i++) {
      printf("addr=%d t[%d]=%s\n",*(t+i),i,*(t+i));
   }
}
void kansu3(char **t)
{
   int i;
   printf("\nkansu3 text=%d\n",t);
   for (i=0;i<4;i++) {
      printf("addr=%d t[%d]=%s\n",*t,i,*t));
   }  //                           ↑=t[]
}
博士「ここをチェックじゃ」

※「char **text 」では、ポインタを入れる1つの領域しか確保されていないので、アドレスを設定してから使用するのじゃ。

実行結果

博士「ここをチェックじゃ」

「char *text[]」は、ポインタの配列じゃったの。
「char **text 」は、()を使って考えるとよいじゃろう。
つまり、「char *(*text))」ということじゃ。
じゃから、*textがchar型へのポインタで、*textへのポインタがtextということじゃな。


困った顔のぷうさん

な、なんか・・・・ごちゃごちゃに・・・・・


kansu1 text=3694
addr=660 t[0]=for
addr=664 t[1]=while
addr=670 t[2]=if
addr=674 t[3]=switch


kansu2 text=3694
addr=660 t[0]=for
addr=664 t[1]=while
addr=670 t[2]=if
addr=674 t[3]=switch


kansu3 text=3694
addr=660 t[0]=for
addr=664 t[1]=while
addr=670 t[2]=if
addr=674 t[3]=switch


main text=3694
addr=664 t[1]=while
addr=670 t[2]=if

チェックポイント

博士「ここをチェックじゃ」

プログラムの実行結果を見れば、*textも**textも同じ取り扱いができることがわかるじゃろ?
たいていポインタヘのポインタは、関数への引数として使われる事が多いんじゃ。
じゃから、引数として**textが出てきたら、*text[]と読み変えて考えるのが一番じゃろう。

但しじゃ、「char **text 」じゃ、ポインタを入れる一つの領城しか確保されんのでな、アドレス指定してから使うんじゃぞ。


ぷうさん「?」

・・・分かったような、分からないような・・・


博士

この本が終わったら、せいぜい問題集で実戦を積むんじゃな。
そのうち分かるようになるじゃろうて。


困った顔のぷうさん

・・・そのうちね・・・


博士「(^^♪」

ふぉっふぉっふぉ、最後にまとめてみようかの。

まとめ

1.文字型変数の宣言と初期値の設定
例:char 文字変数名=’A’
2.文字列の宣言と初期値の設定
例:char 文字配列名[]=”ABC”
(最後に/nが自動的につく)
3.文字列は先頭アドレスをもっている
(ポインタ=”ABC”といった書き方ができる。)

注意点

  1. ‘A’と”A”は意味が異なる
    ・・・”A”は最後に\0がついている
  2. 2次元配列とボインタ配列は、微妙に異なる
    ・・・ポインタの方がメモリの使用効率がいい

博士「Good!」

以上じゃ!わかったかの?


困った顔のぷうさん

ほえほえ・・・・ブレーカーが上がってしまったよお~・・・全ての機能は停止します・・・繰り返します・・・
機能停止・・・・・zzzzzzzzzzzzz


困った顔の博士

・・・まあ予定の所までいったからよくやったというべきか、それともお疲れというべきじゃろうか・・・。


四日目 文字列について 目次

三日目へ

ぷうさんの七日間戦争 四日目

五日目へ