第11講 関数と配列
第4話 配列を関数に渡す
解答例
using namespace System;
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
int y[10][10];
int i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
x[i][j]=10*i+j+1;
}
}
Console::WriteLine("自然配列を表示");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
Console::Write("{0,4:D}",x[i][j]);
}
Console::WriteLine();
}
Console::WriteLine("自然配列を置換");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
y[i][j]=x[j][i];
}
}
Console::WriteLine("自然配列置換を表示");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
Console::Write("{0,4:D}",y[i][j]);
}
Console::WriteLine();
}
}
(コピーペースト用
using namespace System;
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
int y[10][10];
int i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
x[i][j]=10*i+j+1;
}
}
Console::WriteLine("自然配列を表示");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
Console::Write("{0,4:D}",x[i][j]);
}
Console::WriteLine();
}
Console::WriteLine("自然配列を置換");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
y[i][j]=x[j][i];
}
}
Console::WriteLine("自然配列置換を表示");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
Console::Write("{0,4:D}",y[i][j]);
}
Console::WriteLine();
}
}
)
以上が解答例ですが、横軸対称移動、縦軸対称移動、中央点対称移動などを加える予定です。
これらを加えなかったとしても、関数int main()は大きすぎます。
どうしたらよいでしょうか。
そうです。色の付いているところを関数として独立させればよいのです。つまり、
using namespace System;
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
int y[10][10];
int i,j;
関数1;
関数2;
関数3;
関数4;
}
のようにしたいわけです。
今回の配列は、グローバル配列(プログラム冒頭で宣言した配列でプログラム全体で有効)ではなく、
mainの中で宣言したローカル配列(関数内でのみ有効で、関数の呼び出しが終わると配列は消滅する)です。
もちろん、今までのように冒頭で宣言してグローバル変数にする手もありますが、
ローカル変数は、関数が呼び出されている間だけ、メモリが割り当てられるのに対して、
グローバル変数では、プログラムが起動している間、メモリが割り当てられています。
この程度のプログラムであれば、メモリの無駄遣いなどまったく心配する必要はありませんが、
学習が進んでいけば、将来メモリをたくさん使うプログラムも組まなければなりません。
是非ともメモリは節約する習慣をつけたいものです。
ですから、今回はグローバル配列ではなく、メモリを節約できるローカル配列のままで行くことにします。
ローカル配列は、関数内でのみ有効ですから、色の部分を関数として独立させた場合、
何らかの方法で、その配列をそれらの関数に渡す必要があります。
といっても配列そのものを渡すことはできません。
そこで、ポインタ変数を利用して、
配列のアドレスであるx[0][0]のアドレスを渡します。
プログラム例を示してみましょう。
using namespace System;
void ds(int* t,int m,int n);
void tn(int* t,int m,int n);
void hj(int* t,int m,int n);
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
ds(&x[0][0],10,10);
Console::WriteLine("自然配列を表示");
hj(&x[0][0],10,10);
tn(&x[0][0],10,10);
Console::WriteLine("自然配列置換を表示");
hj(&x[0][0],10,10);
}
void ds(int* t,int m,int n){
char i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
*(t+10*i+j)=10*i+j+1;
}
}
}
void hj(int* t,int m,int n){
char i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++){
Console::Write("{0,4:D}",*(t+10*i+j));
}
Console::WriteLine();
}
}
void tn(int* t,int m,int n){
char i,j;
int y[10][10];
for(i=0;i<m;i++){
for(j=0;j<n;j++){
y[i][j]=*(t+10*i+j);
}
}
Console::WriteLine("自然配列を置換");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
*(t+10*i+j)=y[j][i];
}
}
}
(コピーペースト用
using namespace System;
void ds(int* t,int m,int n);
void tn(int* t,int m,int n);
void hj(int* t,int m,int n);
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
ds(&x[0][0],10,10);
Console::WriteLine("自然配列を表示");
hj(&x[0][0],10,10);
tn(&x[0][0],10,10);
Console::WriteLine("自然配列置換を表示");
hj(&x[0][0],10,10);
}
void ds(int* t,int m,int n){
char i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
*(t+10*i+j)=10*i+j+1;
}
}
}
void hj(int* t,int m,int n){
char i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++){
Console::Write("{0,4:D}",*(t+10*i+j));
}
Console::WriteLine();
}
}
void tn(int* t,int m,int n){
char i,j;
int y[10][10];
for(i=0;i<m;i++){
for(j=0;j<n;j++){
y[i][j]=*(t+10*i+j);
}
}
Console::WriteLine("自然配列を置換");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
*(t+10*i+j)=y[j][i];
}
}
}
)
ds(int* t,int m,int n); ←→ ds(&x[0][0],10,10);
の2つを対比させてください。
x[0][0]のアドレス&x[0][0]をポインタ変数tに渡しています。
ポインタ変数は今までint *tで宣言してきましたが、int* tと宣言してもよいのです。
つまり、int* tとint *tはまったく同じものです。
今回int* tとしたのは、tの部分がポインタ変数であることを強調するためです。
次の10,10では、行数と列数を渡しています。
渡した先では、配列はポインタ変数になっているので、ポインタ変数の中身をいじるには、
*(t+10*i+j)のようにしなければなりません。
尚、void ds(int* t,int m,int n)はvoid ds(int* x,int p,int q)としても何の影響も受けません。
使う変数名は何でもいいのです。
理由は、変数は関数の中でのみ通用するローカル変数なので、名前が重なってもそうでなくても関係ないのです。
例えば、次のように書いても
using namespace System;
void ds(int* v,int a,int b);
・
・
・
void ds(int* p,int c,int d){
char i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<c;i++){
for(j=0;j<d;j++){
*(p+10*i+j)=10*i+j+1;
}
}
}
・
・
・
実行結果はまったく同じです。
渡した先の関数ではポインタ変数をつかって、変数の中身を操作していますが、
元の配列が変わっていることをソースに下に示したようにピンクを加えて確認してください。
using namespace System;
void ds(int* t,int m,int n);
void tn(int* t,int m,int n);
void hj(int* t,int m,int n);
int main(){
Console::WriteLine("Visual C++コンソールアプリケーションの世界にようこそ!");
int a,b;
a=10;
b=20;
Console::WriteLine("{0} {1}",a,b);
int x[10][10];
ds(&x[0][0],10,10);
Console::WriteLine("自然配列を表示");
hj(&x[0][0],10,10);
tn(&x[0][0],10,10);
Console::WriteLine("自然配列置換を表示");
hj(&x[0][0],10,10);
Console::WriteLine("配列xを表示");
char i,j;
Console::WriteLine("自然配列を作成");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
Console::Write("{0,4:D}",x[i][j]);
}
Console::WriteLine();
}
}
・
・
・
実行結果
確かに最初のx[2][3]とx[3][2]等が入れ替わっています。
理由は、2次元配列といっても
x[0][0],x[0][1],x[0][2],・・・,x[0][10],x[1][0],x[1][1],x[1][2],・・・,x[1][10],x[2][0],x[2][1],x[2][2],・・・,x[9][10],x[10][0],x[10][1],x[10][2],・・・,x[10][10]
のようにアドレスは1次元に並んでいいるからです。
つまり、アドレスとその内容を表にすると
&x[0][0] | &x[0][0]+1 | ・・・ | &x[0][0]+10 | &x[0][0]+11 | ・・・ |
x[0][0] | x[0][1] | ・・・ | x[1][10] | x[2][0] | ・・・ |
となります。
では皆さん、置換の他に、置換+左右対称移動、置換+左右対称移動+上下対称移動、置換+左右対称移動+上下対称移動+中心点対称移動をさせて、
それぞれを表示させるプログラムを、作りましょう。
置換+左右対称移動+上下対称移動+中心点対称移動は最初の状態に戻っていることがわかります。
第10講第13話へ 第11講第3話へ 第11講第5話へ 第12講第1話へ
vc++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ