ヘッダーファイルから二次元配列を取り込みたいのですが、上手く取り込めません。うまく取り込む方法を詳しく教えて下さい。お願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

 そもそも「ヘッダファイルから二次元配列を読み込む」という発想自体がよくないですね。


 Fookyさんも言われている通り、通常ヘッダファイルに定義は書きません。(C++では例外も多々ありますが)

 恐らくメインとなるソース以外の場所にあるマスターデータを、メインソースから使う方法を知りたいのでしょうが、マスターデータそのものを参照して使うのか、ローカル変数にコピーして使うのかということがはっきりしません。
 Fookyさんは前者を前提に回答されているので、私は後者を前提に回答させていただきます。

・どちらの場合にせよ、データ及び関数の定義はソースファイルに書きます。
 この場合、他のソースからも参照できるようにするための宣言をした、ヘッダファイルも必要になります。
・またマスターデータを、毎回ローカルな二次元配列として初期化するのはムダが多いので、グローバル宣言をして初期化します。
・マスターからデータを取得する関数ですが、Cでは配列を返すことはできないので、配列のポインタを渡し、値をセットするように実装します。
・二次元配列を引数に取る場合、ムリにポインタ表現しなくても、配列表現が許されているので、そのように書きましょう(ROW側は指定してもしなくても無視されます)。

/* data.h */
#define ROW 10
#define COL 10

void init_nijigen(char [ROW][COL]);

/* data.c */
#include <string.h>
#include "data.h"

static const char master[ROW][COL] = {
  {・・・},
    :
};

void init_nijigen(char p[ROW][COL])
{
  memcpy(p, master, ROW * COL);
}

/* main.c */
#include <stdio.h>
#include "data.h"

int main(void)
{
  int ir, ic;
  char nijigenn2[ROW][COL];

  init_nijigen(nijigenn2);

  for (ir = 0; ir < ROW; ir++) {
    for (ic = 0; ic < COL; ic++)
      printf("%c", nijigenn2[ir][ic]);
    putchar('\n');
  }

  return 0;
}

※補足ですが、Fookyさんのソースは、file.cをfile.cppに変えないと、通常の環境ではコンパイルエラーになります。
(Cでは、配列のサイズ指定には定数しか指定できません。const変数が許されるのはC++の言語仕様です。)
    • good
    • 0

確かにCで2次元配列を扱うのは少し難しいんですが、それ以前に色々と問題


があるコードですね。

1.関数file()の戻り値がvoidになっている
 これは2次元配列を返す関数のつもりで書かれているんではないんでしょう
か?それならば、void(戻り値なし)ではなく、2次元配列に相当するものを
返すように宣言しなければなりません。関数の戻り値の指定(void)とreturnの
中に書く変数の型は当然ながら一致していないといけません。

2.それじゃ、2次元配列をどうやって返すのか?
 ここが難しいところなんですが、Cでは配列を一つのモノ(オブジェクト)
として扱うという考え方はしません。RAMの中で配列が格納されているメモ
リの場所(アドレス)を受け渡しします。この、アドレスを入れる変数をポイ
ンタと言います。したがって、file()関数は、10×10配列が格納されてい
るアドレスを指すポインタを返す関数にしないといけません。こんな説明を書
き出すと切りがないので、答え(と言ってもまだまだ間違えています)を書く
と、

char (*file())[10]
{
char nijigen[10][10] = {
{'#','#','#',...,'#'},
....,
{'#','#','#',...,'#'}};

return(nijigen);
}

配列を指すポインタ変数とそれを返す関数の書き方は、

char (*変数名)[10]:「10個のchar型が格納されているメモリを指すポインタ」
          型の変数。
char (*関数名(関数の引数))[10]:「10個のchar…ポインタ」型を返す関数

となります。

3.file()の戻り値を受けるポインタ変数の宣言
 nijigenの宣言は合ってましたが、char nijigen2[10][10];は間違いです。
こう書くと、Cは、新たに10×10の配列をメモリ上に用意します。今、や
りたいことはそれではないはずです。file()の中で用意された10×10配列
を扱いたいんですから。

nijigen[10][10]が正解なのは、nijigen[10][10]のところでは実際に
メモリ上に配列を用意するから正解なのです。

正解は、
char (*nijigen2)[10];
nijigen2 = file();

4.表示が転置されてる?
 printf("%c", nijigen2[a][b]);
となってますが、これだと縦がa+1番目、横がb+1番目の要素が
表示されますが良いんですか?

5.局所変数を戻り値にしている
 これは大問題です。file()の中の変数は、file()を出るときに全て解体され
ます。ということは、file()の中でメモリ上に作った10x10配列は、file()か
らreturnするときに消えてなくなります。したがって、ここは、nijigenを大
域変数として定義するのが良いでしょう。

~file.h~
...
static char nijigen[][10] = {
{'#', ..., '#'},
...};

6.ヘッダファイルの使い方がおかしい?
 ヘッダファイルには普通、関数の中身(定義)は書きません。もう少し規模
の大きいプログラムになると即問題が発生します。ヘッダファイルを複数の.c
ファイルから読み込んだときに、同じ名前の関数が2箇所で重複して定義され
ていることになります。変数も同様です。上のstaticというのは、この変数は
今の.cファイルの中だけの話しですよ、ということを示すことによって、
file.hが複数の.cファイルから読まれても大丈夫なようにしています。(その
代わり、読まれた回数だけメモリを消費します)

ふぅ。何にせよ、本を一冊買ってポインタの辺りを勉強することをお勧めしま
す。

私がこのコードを書くと下のように成ります。当然ながらベストとは言いませ
ん。私の好みということです。2次元配列をつくるための「10個のchar…ポイ
ンタ」はできるだけ使わない方が、char (*nijigen2)[10];みたいなややこし
い宣言を使わなくて済みます。

~file.h~

static const int RowNum = 10;
static const int ColNum = 10;

static const char nijigen[RowNum][ColNum] = {
{...},
...
};

~file.c~

int
main(int argc, char **argv)
{
int r, c;
const char *pointer = nijigen[0];

for( r = 0 ; r < RowNum ; r++ ){
for( c = 0 ; c < ColNum ; c++, pointer++ )
printf("%c", *pointer);
printf("\n");
}
}
    • good
    • 0

こんにちは。

 itohhといいます。

どのように、取り込もうとしたのかもう少し書いてください。
(2次元配列の定義部分や#include文の書き方など)

あと、うまくいかないとはどうなるのでしょうか?
(コンパイルエラーになる?実行時にうまく参照できない?)
このへんのことも、詳しく書いた方がアドバイスしやすいですよ。

この回答への補足

~file.h~
#include<stdio.h>
void file(){
char nijigenn[10][10]={{'#','#'・・・・・・'#','#'},
{'#','#'・・・・・・'#','#'},



{'#','#'・・・・・・'#','#'}};
return(nijigenn);
}
~file.c~
#include<stdio.h>
#include"file.h"
main(){
int a,b;
char nijigenn2[10][10];
nijigenn2=file();
for(b=0;b<10;b++){
for(a=0;a<10;a++){
printf("%c",nijigenn2[a][b])
}}
}
こんな感じで作ってみたんですけど、コンパイルが上手くできませんでした。
よろしくお願いします。

補足日時:2001/05/26 23:40
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q二次元配列による文字列の配列の受渡しについての質問です。

二次元配列による文字列の配列の受渡しについての質問です。

#include <stdio.h>

void print_pname(char str[][5], int n)
{
int i, j;
for (i = 0; i < n; i++)
{
printf("str[%d] = \"", i);
for (j = 0; str[i][j] != '\0'; j++)
putchar(str[i][j]);
printf("\"\n");
}
}

int main(void)
{
char ary[][5] = {"Lisp", "C", "Ada"};

print_pname(ary, sizeof(ary) / sizeof(ary[0]));
return 0;
}

上のプログラム中の関数print_pnameの引数char str[][5]についてですが
char (*str)[5](配列のポインタ)と変更した場合にwarningが多数発生します。
これはどうしてでしょうか?
また、上のプログラムを配列のポインタを使って変更することは可能でしょうか?

以上、よろしくお願いします。

二次元配列による文字列の配列の受渡しについての質問です。

#include <stdio.h>

void print_pname(char str[][5], int n)
{
int i, j;
for (i = 0; i < n; i++)
{
printf("str[%d] = \"", i);
for (j = 0; str[i][j] != '\0'; j++)
putchar(str[i][j]);
printf("\"\n");
}
}

int main(void)
{
char ary[][5] = {"Lisp", "C", "Ada"};

print_pname(ary, sizeof(ary) / sizeof(ary[0]));
return 0;
}

上のプログラム中の関数print_pnameの引数char str[][5]についてですが
char (*st...続きを読む

Aベストアンサー

いや, 確かにそこはおかしいけど (注: どっちにしても未定義動作ですがそこはスルーしてた), while の条件が変です>#6. このまままわると *ary[3] を参照しちゃいます.
で本題に戻ると,
char (*ary)[5] = {"Lisp", "C", "Ada"};
は ary を「『大きさ 5 の配列』へのポインタ」として定義しています. ポインタはスカラーですから, このように複数の値を使って初期化することはできません.
なお, GCC (や ISO C に従っている処理系) なら
char (*ary)[5] = (char [][5]){"Lisp", "C", "Ada"};
で初期化できるかもしれません. でも, そのあとがどうせダメか.

Qテキストファイルを二次元配列に

お世話になります。

テキストファイルを1行ずつ読み込んで二次元配列に格納するプログラムですが、

//最大行数
#define LINE_MAX 10
//行内最大文字数
#define INPUT_MAX 128
char str[LINE_MAX][INPUT_MAX];

というようにして実現しています。

これを行数が分からないテキストファイルでも大丈夫なようメモリを動的に確保したいと考えています。

二つの次元の内、一つを動的に確保するにはどのようにしたら良いでしょうか。

Aベストアンサー

文字数固定なら。。。
・先に行数のみを取得しその分だけ、領域を確保して読み込む
・ある程度の領域を用意し、足りなくなったらある程度追加確保する
とかとか

考え方は前者の方が理解しやすいですが、動作的な短所には、
前者はファイル全部を2回読むことになるので無駄。
後者は領域を確保する間隔を大きくすると未使用の領域が出来てしまい、小さくし過ぎると頻繁にメモリ確保を行う可能性があるなど。

以下は10行ずつ領域を追加しながら500行まで、
表示するサンプル。(空白は全角)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXINPUTNUM (1024)

int main ( void )
{
 int i, cnt, row = 10;
 char (*StringTable)[MAXINPUTNUM];
 char TempBuffer[MAXINPUTNUM];

 StringTable = (char(*)[MAXINPUTNUM])calloc( row, sizeof(char)*MAXINPUTNUM );
 if ( StringTable == NULL )
 {
  return 1;
 }

 for ( i = 0; i < 500; i++ )
 {
  if ( row <= i )
  {
   row+=10;
   StringTable =(char(*)[MAXINPUTNUM])realloc( StringTable, (row*(sizeof(char)*MAXINPUTNUM)));
  }
  sprintf( TempBuffer, "%d行目\n", i + 1 );
  strcpy( StringTable[i], TempBuffer );
 }

 cnt = i;
 for ( i = 0; i < cnt; i++ )
 {
  printf( "%s", StringTable[i] );
 }

 free( StringTable );
 StringTable = NULL;
 return 0;
}

文字数固定なら。。。
・先に行数のみを取得しその分だけ、領域を確保して読み込む
・ある程度の領域を用意し、足りなくなったらある程度追加確保する
とかとか

考え方は前者の方が理解しやすいですが、動作的な短所には、
前者はファイル全部を2回読むことになるので無駄。
後者は領域を確保する間隔を大きくすると未使用の領域が出来てしまい、小さくし過ぎると頻繁にメモリ確保を行う可能性があるなど。

以下は10行ずつ領域を追加しながら500行まで、
表示するサンプル。(空白は全角)

#include ...続きを読む

Q二次元配列を引数として渡し、再び返す方法

c++を用いてます。
main関数内において、二次元配列(行列)の計算によって各成分の値を求め、
その二次元配列を関数の引数として渡し、成分を用いて計算した後、再び返す方法を知りたいです。

具体的には、ルンゲクッタ法を用いたプログラムで、

#include<iostream>


double g11(double D[][9],double x11~)
double g12(double D[][9],double x11~)


int main(void)
{
(中略)double D[6][9]=などの定義をしています

for(i=0;i<100;i+)
{
行列の計算(積や逆行列の計算)

ルンゲクッタ法(ここでg11を用います)
k111=h*g11(D,x11,…);

dx11=(k111+…)/6.0;


cout<<x11…<<endl;

x11+=dx11;
x12+=dx12;


}
return 0;
}

double g11 (double D[][9],double x11…)
{
double dif;
dif=D[0][0]*x11+D[0][1]*x12+…
  return(dif);
}

double g12 (double D[][9],double x11…)
{
double dif;
dif=D[1][0]*x11+D[1][1]*x12+…
  return(dif);
}


というようなプログラムです。エラーは出ないのですが、値が一瞬で発散してします。
ちなみに、D[6][9]は行列の積や逆行列の計算を行った結果得られた二次元配列です。
おそらくポインタをうまく使わないといけないことはなんとなく解るのですが…。


プログラムの説明が解りにくくて申し訳ないのですが、よろしくお願いいたします。

c++を用いてます。
main関数内において、二次元配列(行列)の計算によって各成分の値を求め、
その二次元配列を関数の引数として渡し、成分を用いて計算した後、再び返す方法を知りたいです。

具体的には、ルンゲクッタ法を用いたプログラムで、

#include<iostream>


double g11(double D[][9],double x11~)
double g12(double D[][9],double x11~)


int main(void)
{
(中略)double D[6][9]=などの定義をしています

for(i=0;i<100;i+)
{
行列の計算(積や逆行列の計算)

ルン...続きを読む

Aベストアンサー

ルンゲクッタ法のことはいったん脇へ置いて、

>二次元配列を関数の引数として渡し、成分を用いて計算した後、再び返す

このことだけを実現するコードを書いてみてはいかがでしょうか。
ルンゲクッタ法の話は、今は傍流ですよね?
大きな問題を小分けにする方が早く解決するのではないかと思います。

Q【C#】二次元配列へのcsvファイルの格納について

いつもお世話になってます。

C#に関して質問です。
VisualC#2008を使用しています。

二次元配列の中にcsvファイルの内容を格納したいと思っています。
一次元配列だと、split(',')のようにコンマで区切って格納することができたのですが、
二次元配列になるとうまくいきません。

作成中のプログラム(一部)は下記のようになっています。

二次元配列になっても、csvファイルをうまく格納できる方法を誰かおしえていただけないでしょうか?
よろしくお願いします。


private void 問題ファイルを読み込むXToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)
{
System.IO.Stream stream;
stream = ofd.OpenFile();
//StreamReaderオブジェクトの作成
StreamReader sr = new StreamReader(stream, Encoding.GetEncoding(932));

string dat; //文字を入れる

while ((dat = sr.ReadLine()) != null)
{

sbuf = dat.Split(','); //エラー箇所

//DataGridViewに新しい行を追加
int r = dataGridView1.Rows.Add();

dataGridView1[0, r].Value = sbuf[mondai_count,0];
dataGridView1[1, r].Value = sbuf[mondai_count,1];
dataGridView1[2, r].Value = int.Parse(sbuf[mondai_count,2]); //Parseで文字列を数値に変換
}
//visible=False でDataGridView非表示
mondai_bun[0] = sbuf[mondai_count,0];
mondai_title[0] = sbuf[mondai_count,1];

mondai_bun2.Text = mondai_bun[0];
this.mondai_list.Items.Add(mondai_title[0]);

//ファイルを閉じる
sr.Close();
stream.Close();
}
}

いつもお世話になってます。

C#に関して質問です。
VisualC#2008を使用しています。

二次元配列の中にcsvファイルの内容を格納したいと思っています。
一次元配列だと、split(',')のようにコンマで区切って格納することができたのですが、
二次元配列になるとうまくいきません。

作成中のプログラム(一部)は下記のようになっています。

二次元配列になっても、csvファイルをうまく格納できる方法を誰かおしえていただけないでしょうか?
よろしくお願いします。


private void 問題ファイル...続きを読む

Aベストアンサー

次は参考になりませんでしょうか?

http://www.oborodukiyo.info/Forms/VS2008/F-ReadCSVToDataTable.aspx

トライしているのはDataGridViewに直接入れているようですが、DataTableに保存しておいて、
dataGridView1.DataSource = dt;
という形でもいけます。

Q二次元配列2

キーボードから0 以上100 以下の整数を入力し,(1)0 以上10 未満,(2)10 以上20 未満,(3)20 以上30 未満,・・・(10)90 以上100 未満,(11)100 の11 段階に分類して頻度を調べるプログラムを作成します。負の値が入力された場合にはプログラムを終了して,101 以上の数が入力された場合には入力値が無効であることを画面表示して頻度には数えない。

日本語が難しくて少し理解しずらいのですが・・・・・
#include <stdio.h>

int main(void){
int i,n,count;
int a[11] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

do{

printf("1以上100以下の整数を入力");
scanf("%d",&n);

/*ここに頻度の処理を書く??*/

}while(n < 0);

printf("終了");
for( i = 0; i < 11; i++){
/*画面表示*/
}

getchar();
getchar();
return(0);
}
という具合でいいのでしょうか・・・・・・・
それと頻度の処理はどうすればいいのでしょうか

キーボードから0 以上100 以下の整数を入力し,(1)0 以上10 未満,(2)10 以上20 未満,(3)20 以上30 未満,・・・(10)90 以上100 未満,(11)100 の11 段階に分類して頻度を調べるプログラムを作成します。負の値が入力された場合にはプログラムを終了して,101 以上の数が入力された場合には入力値が無効であることを画面表示して頻度には数えない。

日本語が難しくて少し理解しずらいのですが・・・・・
#include <stdio.h>

int main(void){
int i,n,count;
int a[11] = { 1, 2, 3, 4, 5, 6, ...続きを読む

Aベストアンサー

>という具合でいいのでしょうか・・・・・・・
>それと頻度の処理はどうすればいいのでしょうか
あまりに何も出来ていないので、もう少し頑張ってから質問した方がよいでしょう。
このままでは配列 a[11] の用途すらわかりません。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング

おすすめ情報