トップページ > 過去ログ > 記事閲覧
多次元配列の構造体のポインタを返す方法
名前:通りすがり 日時: 2008/02/25 00:02

例えばですが
STest Test[2][2];
という構造体があって、
STest** Return()
{
   return Test;
};
と言う形で返したいのですが、
STest[2][2]' から STest**' に変換できません。
という形でエラーが出てしまいます。
わかりにくくて申し訳ありません。
エラーが出ずにすむ方法があればおしえていただきたく
思います。よろしくお願いします。

Page: 1 |

Re: 多次元配列の構造体のポインタを返す方法 ( No.1 )
名前:tok 日時:2008/02/25 10:49

return (STest**)(&Test);
でエラーは出なくなりますが、確実にその戻り値を使うと問題が発生すると思います。
ローカル変数から返したい場合は、
STest* Test;
Test = (STest*)malloc(sizeof(STest)*2*2);
として、ポインタとしてTestを宣言してreturn Test;としてやればいいと思います。
Re: 多次元配列の構造体のポインタを返す方法 ( No.2 )
名前: 日時:2008/02/25 17:15

>Test = (STest*)malloc(sizeof(STest)*2*2);
これは多次元配列ではなく1次元配列です。
Testは単純にSTest[4]と同じ連続領域が確保されます。
#N次元配列は連続したメモリ配置上での差異は
#ないと思いますが上記のような返却では、
#返却した先での使用方法としてTest[1][1]のような
#アクセス方法が出来ない為。

C言語では関数が配列を返す事は出来ない為、
ポインタにしますが、多次元配列の場合、
型をしっかり考える必要があります。

STest Test[2][2];
という定義に基づくと
Testの型はSTest[2][2];
&Testの型はSTest(*)[2][2]
となります。

あゆしゃさんのHPでは動的に多次元配列を
作成する方法が載っています。
ttp://ayusya.hp.infoseek.co.jp/ProgramAlgorithm.html
ただし、これらの配列は連続した領域ではないので
解放する場合やポインタ計算する場合などは注意が必要です。

固定的な2次元配列などは
以下のような方法でも可能です。
typedef struct test{
    int hogehoge;
}TEST;
typedef TEST (*TEST2ARY)[2];
TEST2ARY alloc_test( void )
{
    TEST (*test2)[2];
    test2 = (TEST(*)[2])malloc( sizeof(TEST[2]) * 2 );
    return test2;
}
int main(void)
{
    int i, j;
    TEST2ARY test2ary;
    test2ary = alloc_test();
    for( i=0; i<2; i++ ){
	for( j=0; j<2; j++ ){
	    test2ary[i][j].hogehoge = i;
	}
    }
    for( i=0; i<2; i++ ){
	for( j=0; j<2; j++ ){
	    printf ( "test2ary[%d][%d].hoge = %d\n", i, j, test2ary[i][j].hogehoge );
	}
    }
    free( test2ary );
    return 0;
}
Re: 多次元配列の構造体のポインタを返す方法 ( No.3 )
名前:tok 日時:2008/02/29 16:28

訂正の方、ありがとうございました。
正確な意味での多次元配列の扱い方は知らなかったので、勉強になりました。
ついで、お手数でなければ知識深めるため、以下のコードに問題はないかお教えください。

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

typedef struct{
	int a;
	char b;
} STest;

STest* Return()
{
	STest (*Test)[2] = (STest(*)[2])malloc(sizeof(STest[2]) * 2);
	Test[0][0].a = 10;
	Test[1][1].b = 11;
	return (STest*)Test;
}
int main()
{
	STest (*test)[2] = (STest(*)[2])Return();
	printf("%d, %d\n", test[0][0].a, test[1][1].b);
	free(test);
	return 0;
}

主に心配なのは、STest*にキャストした時点でアドレスがずれたりする
危険性はないかどうかに関して、です。
ウチの環境(.NET2003)では

STest* hoge = (STest*)Test;
printf("%p\n", Test);
printf("%p\n", hoge);

で検証しましたが、同じアドレスでした。
これが環境依存な結果であった場合、上のコードは不適切となりますよね。
Cの仕様的にどうなのかが知りたいです。(K&R〜C99)
ご存知の範囲で構わないですので、よろしくお願いします。
Re: 多次元配列の構造体のポインタを返す方法 ( No.4 )
名前: 日時:2008/02/29 17:10

ポインタ自体のサイズはどんなオブジェクトの型の
ポインタであっても同じ環境であれば一律です。
STest* Return()
を
void* Return()
のように考えるとわかりやすいかもしれません。
キャストはコンパイラをだましているだけなので、
少し言い過ぎかもしれませんが、Cの仕様を無視
させているという認識のほうが良いかと。

一番安全なのは引数でとると良いかもしれません。
int Return( STest (**Test)[2] )
{
	if ( Test==NULL ){ return 1; }
	(*Test) = (STest(*)[2])malloc(sizeof(STest[2]) * 2);
	if ((*Test)==NULL){ return 2; }
	(*Test)[0][0].a = 10;
	(*Test)[1][1].b = 11;
	return 0;
}
int main()
{
	int ret;
	STest (*test)[2];

	ret = Return( &test );
	if ( ret ){ return 1; }

	printf("%d, %d\n", test[0][0].a, test[1][1].b);
	free(test);
	return 0;
}
これなら何故失敗したかの詳細も返せますし。

Re: 多次元配列の構造体のポインタを返す方法 ( No.5 )
名前:tok 日時:2008/02/29 21:19

ありがとうございました。
ポインタの配列であれば問題がなさそうに感じたんですけど、
配列へのポインタというのはどうなんだろうと考えていました。
けどよく考えたら、結局ポインタはポインタなんですよね。
配列へのポインタかどうかという情報は、単にコンパイラが区別する
ためにだけあって、其れは一意のアドレスを表現する変数に過ぎないという。

いただいたサンプルコードの方は、読んでなるほどと思いました。
とはいえ、実は僕も通りすがりさんの形に合わせてみただけで、自分で
やるとしたら引数に取ると思いますし、freeも生でなく、
何かでwrapして使うと思います。
割り込みの質問に答えていただいてありがとうございました。
ついで、一連の情報が通りすがりさんのために役立つことをお祈りします。

Page: 1 |