- PR -

【C言語】二次元配列の動的確保

1
投稿者投稿内容
ぴんふ
ベテラン
会議室デビュー日: 2006/07/13
投稿数: 80
投稿日時: 2007-03-26 12:01
こんにちは。ぴんふです。

C言語の質問で恐縮です。どなたかお答え頂けたら幸いです。

行数不明のテキストファイルから1行ずつデータを取得して文字列の配列に
データを取得したいので、動的に領域を拡張していきたいのですが、うまくいきません。

ソースはこんな感じです。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void jikken(char *pszFileName,char pszBuff[][4+1],short *pnOutNum);

void main()
{
char szBuff[1][4+1];
short nRecNum = 0;
short i = 0;

printf("START\n");

jikken("Test.txt",szBuff,&nRecNum);
for(i = 0;i < nRecNum;i++){
printf("[%d][%s]\n",i,szBuff[i]);
}

for(i=0;i < nRecNum;i++)
{
printf("[%d][%s]\n",i,szBuff[i]);
}

free(szBuff);
printf("END\n");
}

void jikken(char *pszFileName,char pszBuff[][4+1],short *pnOutNum)
{
FILE *fp;
char szLine[4+1+1];
short nCnt = 0;
short i = 0;
char **szTmp = NULL;

fp = fopen(pszFileName,"r");

while(NULL != fgets(szLine,(4 + 1 + 1),fp)){
if(szLine[0] == '\n'){
break;
}else{
szLine[4] = '\0';
}

/* ここでメモリを拡張したいが落ちる・・・ */
pszBuff = realloc(pszBuff,4+1 * nCnt);

strcpy(&pszBuff[nCnt][0],szLine);

nCnt++;
}

*pnOutNum = nCnt;

fclose(fp);
}

C#とかなら簡単なことがCだといろいろ大変なので・・・
よろしくお願いします。
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2007-03-26 12:18
引用:

ぴんふさんの書き込み (2007-03-26 12:01) より:
行数不明のテキストファイルから1行ずつデータを取得して文字列の配列に
データを取得したいので、動的に領域を拡張していきたいのですが、うまくいきません。


C言語に配列を動的に拡張する機能なんてありません。reallocはmallocで確保したヒープメモリを動的に拡張する命令です。それ以外の方法で確保したメモリに対しては使えません。
_________________
甕星 <mikahosi@abox9.so-net.ne.jp>
http://blogs.msmvp.jp/mikahosi/
ぴんふ
ベテラン
会議室デビュー日: 2006/07/13
投稿数: 80
投稿日時: 2007-03-26 12:24
甕星さん、レスありがとうございます。

確かに配列の動的確保の機能はないので、
代替的な方法があったら教えていただきたいと思って投稿しました。
言葉足らずで申し訳ございません。

sanderi
会議室デビュー日: 2007/03/26
投稿数: 3
投稿日時: 2007-03-26 13:19
はじめまして。
C言語で単純に2次元配列を動的に使いたいのであれば、配列型をtypedefすることで比較的わかりやすく使うことが出来ます。


typedef char CHAR5[5]; /* 要素数5の文字配列型 */

・・・

CHAR5 *szBuff;
szBuff = (CHAR5 *)malloc(5); /* 1行分のメモリ確保 */
realloc(szBuff, 5 * 10); /* 10行分に拡張 */

szBuff[0][0] = 'a'; /* 使うときは通常の2次元配列のように使える */

reallocのようなメモリの再割り当ては頻繁に行うとパフォーマンスが低下するため、最初に行数をカウントしてからmallocで一気に取得しておいたほうが良いと思います。
ぴんふ
ベテラン
会議室デビュー日: 2006/07/13
投稿数: 80
投稿日時: 2007-03-26 14:18
sanderiさんありがとうございます。

配列型をtypedefにしてmalloc→reallocで実現できましたが、
その配列型を引数とする関数を作り実行してみると、その関数から
戻ったところでその配列型を参照するところでメモリ違反となります。

変数の渡し方が良くないと思われますが・・・。
どうしたものでしょうか?


以下はそのソースです。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef char CHAR5[5];

void jikken(char *pszFileName,CHAR5 *pszBuff,short *pnOutNum);

void main()
{
CHAR5 *szBuff = NULL;
short nRecNum = 0;
short i = 0;

printf("START\n");

jikken("Test.txt",szBuff,&nRecNum);

for(i=0;i < nRecNum;i++)
{
/* ここで落ちる */
printf("[%d][%s]\n",i,szBuff[i]);
}

free(szBuff);
printf("END\n");
}

void jikken(char *pszFileName,CHAR5 *pszBuff,short *pnOutNum)
{
FILE *fp;
char szLine[4+1+1];
short nCnt = 0;
short i = 0;
char **szTmp = NULL;

fp = fopen(pszFileName,"r");

while(NULL != fgets(szLine,(4 + 1 + 1),fp)){
if(szLine[0] == '\n'){
break;
}else{
szLine[4] = '\0';
}
if(nCnt == 0){
pszBuff = (CHAR5 *)malloc(5);
}else{
pszBuff = (CHAR5 *)realloc(pszBuff,(4+1) * (nCnt+1));
}
strcpy(pszBuff[nCnt],szLine);

nCnt++;
}

*pnOutNum = nCnt;

fclose(fp);
}

sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2007-03-26 15:10
引用:

ぴんふさんの書き込み (2007-03-26 14:18) より:
配列型をtypedefにしてmalloc→reallocで実現できましたが、
その配列型を引数とする関数を作り実行してみると、その関数から
戻ったところでその配列型を参照するところでメモリ違反となります。



jikkenの中で引数のpszBuffに受け取ったアドレスに対してreallocを行っています。
reallocから返された新しいアドレスをpszBuffに代入していますが、その変更は
jikkenの呼び出し元には伝わりません。
そのため、呼び出し元ではreallocですでに解放されてしまった古いアドレスにアクセスしてメモリ違反となるのだと思います。

なので、jikkenから新しいアドレスが呼び元に返せるようにすればよいと思います。
(ポインタのポインタにするか、戻り値で返すか)


# C言語は大学の演習で使ったぐらいで、reallocも使ったことないですけど…
sanderi
会議室デビュー日: 2007/03/26
投稿数: 3
投稿日時: 2007-03-26 15:18
関数の値渡しとポインタ渡しについて学習してください。

この場合関数の引数は
CHAR5 *szBuff = NULL;
で設定されたNULLという値(0)を渡しているに過ぎません。
jikken側のpszBuffにもNULLが入りますが、器はszBuffとは別物です。
つまりpszBuffのポインタを変更しても呼び出し側のszBuffはNULLのままです。

呼び出し側にポインタの変更を教えるためにはダブルポインタを使うか、戻り値にポインタを返して呼び出し側で受けてあげる必要があります。

void main()
{
・・・
jikken("Test.txt", &szBuff, &nRecNum);
・・・
}

void jikken(char *pszFileName,CHAR5 **pszBuff,short *pnOutNum)
{
・・・
if(nCnt == 0){
*pszBuff = (CHAR5 *)malloc(5);
}else{
*pszBuff = (CHAR5 *)realloc(*pszBuff, (4+1) * (nCnt+1));
}
strcpy((*pszBuff)[nCnt], szLine);
・・・
}

デバッガを使えばszBuffがNULLのままであることはすぐに見えるはずです。
質問の前にある程度は手は尽くしてみましょう。
ぴんふ
ベテラン
会議室デビュー日: 2006/07/13
投稿数: 80
投稿日時: 2007-03-26 15:23
【解決】
sanderiさんsawatさんありがとうございます。

ここ数年Cから離れていたので・・・かなりトンチンカンな
ことを言ってました。申し訳ございません。

ポインタを関数の戻り値で返すことでうまくいきました。

これを機に勉強しなおしてきます。

重ね重ねありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)