C言語関係掲示板

過去ログ

No.1175 ポインタの型を場合分けで変える

[戻る] [ホームページ]
No.15292

ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/06 19:35:32)


こんにちは。
いつもお世話になっております。

下記の様に、ポインタの型を、kindの値によって変えたいのですが、
これですと、メンバを参照する部分で「定義されていないエラーです。」
とエラーが出てしまいます。
最初に適当に
A *p_in = NULL;
A *p_out = NULL;
で宣言するとAccessViolationで落ちてしまいます。
このようなことはできないのでしょうか?

●A, B, Cは構造体で、それぞれのメンバ名は同じにしてあります。
●環境は、VisualStudio6.0です。

int function(char *inbuf, char *outbuf, int kind)
{
    switch (kind){
        case 1:
            A *p_in = (A *)inbuf;
            A *p_out = (A *)outbuf;
            break;
        case 2:
            B *p_in = (B *)inbuf;
            B *p_out = (B *)outbuf;
            break;
        case 3:
            C *p_in = (C *)inbuf;
            C *p_out = (C *)outbuf;
            break;
    }

    /* 処理いろいろ */
    function2(p_in->member1, p_out->member1);
    function3(p_in->member2, p_out->member2);
    ・・・
}



No.15293

Re:ポインタの型を場合分けで変える
投稿者---REE(2004/07/06 19:51:36)


>下記の様に、ポインタの型を、kindの値によって変えたいのですが、
>このようなことはできないのでしょうか?

できません。
{}の中で宣言した変数は{}の中でだけ有効です。

結論から言うと、たとえ、メンバの名前が同じでも、それぞれ別の型の構造体であれば、同じように操作することは出来ません。

ねこのひげさんの考えている方法では、メンバにアクセスする度に、種類別にポインタをキャストする必要があります。


「バイナリデータを数値に変換」で私が言った「かなりややこしいことになりそうです・・」はこういうことです。





No.15295

Re:ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/06 20:05:47)


REE様、いつもありがとうございます。

>できません。

そうでしたか・・・。ありがとうございます。
素直に使用する構造体によって、関数を分けます・・・。

>「バイナリデータを数値に変換」で私が言った「かなりややこしいことになりそうです・・」はこういうことです。

こういった事も含まれていたのですね。納得です。
精進致します。ありがとうございました。



No.15296

Re:ポインタの型を場合分けで変える
投稿者---shu(2004/07/06 20:12:30)


共用体
http://www9.plala.or.jp/sgwr-t/c/sec16.html#s16-2

は使えませんか?


No.15333

Re:ポインタの型を場合分けで変える
投稿者---YuO(2004/07/07 21:54:23)


> ●A, B, Cは構造体で、それぞれのメンバ名は同じにしてあります。

同じメンバ名で型も同じですか?
それであれば,共通部分を構造体にして,A, B, Cではなくその共通部分を渡すようにすればよいです。
#この延長線上に,VC++におけるクラスの実装が存在します。

そうでないなら,
     function2(p_in->member1, p_out->member1);
     function3(p_in->member2, p_out->member2);
をマクロで展開させるしかなくなります。



No.15430

Re:ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/11 23:56:56)


shu様、YuO様、ありがとうございます。
また、返信が遅くなりまして申し訳ございません。

共用体は、今までどういう時に使うのか分からなかったのですが、
このような時に使うのですね!
早速、共用体を使って書いてみたのですが、、、
また分からなくなってしまいました。すみません。
下記のようにすると、
function2やfunction3を呼び出す時の引数をどう書けばよいのでしょう?
それとも、そもそも、共用体の使い方を勘違いしていますか?
---------------
typedef struct
{
    char member1[1];
    char member2[2];
    char member3[3];
} A;

typedef struct
{
    char member1[5];
    char member2[6];
    char member3[7];
} B;

typedef struct
{
    char member1[1];
    char member2[3];
    char member3[5];
} C;

function(char *, char *, int);

void main()
{
    char char_in[] = "abcdefghijklmnopqr";
    char char_out[20];
    function(char_in, char_out, 1);
}

int function(char *inbuf, char *outbuf, int kind)
{
    union IN{
        A *p_in_a;
        B *p_in_b;
        C *p_in_c;
    };
    union OUT{
        A *p_out_a;
        B *p_out_b;
        C *p_out_c;
    };
    union IN in;
    union OUT out;

    switch (kind){
        case 1:
            in.p_in_a = (A *)inbuf;
            out.p_out_a = (A *)inbuf;
            break;
        case 2:
            in.p_in_b = (B *)inbuf;
            out.p_out_b = (B *)outbuf;
            break;
        case 3:
            in.p_in_c = (C *)inbuf;
            out.p_out_c = (C *)outbuf;
            break;
    }

    /* 処理いろいろ */
    function2(???->member1, ???->member1);
    function3(???->member2, ???->member2);
    ・・・
-----------

>同じメンバ名で型も同じですか?
>それであれば,共通部分を構造体にして,A, B, Cではなくその共通部分を渡すようにすればよいです。

上記のように、型は同じですが、サイズが違います。
ということは、共通部分もないので、この方法は使えないですよね。

>マクロで展開させるしかなくなります。

共用体を使用して、かつ、
function2、function3の引数をマクロで宣言した値にする、
ということでしょうか?
------------
#define ARG_IN(x) p_in_x
#define ARG_OUT(x) p_out_x
として、case文の中で、
function2(ARG_IN(a)->member1, ARG_OUT(a)->member1);
------------
これだと、意味ないですよね・・・。
すみません、どのようなマクロを宣言&使用すれば良いのか、
教えていただけますでしょうか。



No.15435

Re:ポインタの型を場合分けで変える
投稿者---YuO(2004/07/12 00:55:11)


> 上記のように、型は同じですが、サイズが違います。

型が同じなのにサイズが異なるということはあり得ません。
char [1]型とchar [5]型は別の型です。
#A::member1はchar [1]型であってchar型ではない。


>> マクロで展開させるしかなくなります。
> 共用体を使用して、かつ、
> function2、function3の引数をマクロで宣言した値にする、
> ということでしょうか?

#undef FUNCTION_IMPLEMENT
#define FUNCTION_IMPLEMENT(type) \
    int function_##type (type * inbuf, type * outbuf) \
{ \
    function2(inbuf->member1, outbuf->member1); \
    function3(inbuf->member2, outbuf->member2); \
    /* ... */ \
}
#undef FUNCTION_INVOKE
#define FUNCTION_INVOKE(type, inbuf, outbuf) \
    function_##type(inbuf, outbuf)

FUNCTION_IMPLEMENT(A)
FUNCTION_IMPLEMENT(B)
FUNCTION_IMPLEMENT(C)

というようにして,FUNCTION_INVOKEを使って呼び出すようにします。

呼び出しコードを変えたくないのであれば,
int function (void * inbuf, void * outbuf, int kind)
{
#undef FUNCTION_IMPLEMENT
#define FUNCTION_INPLEMENT(type) \
    do { \
        function2((type *)inbuf->member1, (type *)outbuf->member1); \
        function3((type *)inbuf->member2, (type *)outbuf->member2); \
        /* ... */ \
    } while (0)

    switch (kind)
    {
    case 1:
        FUNCTION_IMPLEMENT(A);
        break;
    case 2:
        FUNCTION_IMPLEMENT(B);
        break;
    case 3:
        FUNCTION_IMPLEMENT(C);
        break;
    }
    return /* ... */ ;
#undef FUNCTION_IMPLEMENT
}
のようになります。

そもそも,型が異なるものを同じコードで動かそう,というところに無理があるのです。
Cでは,名前が同じであっても大して意味をもちません。
今回の場合,素直にC++のtemplateを使うべきだと思います。
最初のマクロは,templateと同じことを強引にCで書いた物です。
ちなみに,C++で書くと,
template <typename T> int function (T * inbuf, T * outbuf)
{
    function2(inbuf, outbuf);
    function3(inbuf, outbuf);
    /* ... */
}
と,非常に読み易く,素直になります。



No.15478

Re:ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/13 12:11:04)


YuO様、shu様、ありがとうございます。

>型が同じなのにサイズが異なるということはあり得ません。
>char [1]型とchar [5]型は別の型です。

そうだったのですか!char[x]型はchar型だと思っておりました。
表現が不適切だったために、余計なお手間を取らせてしまいまして、
申し訳ございませんでした。

>#undef FUNCTION_IMPLEMENT
>・・・
>#undef FUNCTION_IMPLEMENT
>}

おお!すごいです!このようなマクロを使うのですね。
まだ、マクロと言うと定義部分の短い、簡単なものしか
思いつくことができず、このように長いものは初めてです。
自分でも、これが思いつくようになるよう、もっと勉強いたします。

>そもそも,型が異なるものを同じコードで動かそう,というところに無理があるのです。

やはり、そうなのですね。すいません。

>今回の場合,素直にC++のtemplateを使うべきだと思います。

VisualStudioを使っているくせに、C++はさっぱり・・・
なのですが、C++も習得したい言語なので、こちらも貴重な情報として、
参考にさせていただきます。

大変有益なレスを、ありがとうございました。

>function(char_in, char_out, 1);
>の処理で、char_outにはどういう文字列が入りますか?

char_inは、上記で書いた例では"abcdef・・・"ですが、
実際には、複数の文字コードやバイナリのデータが混ざった文字列です。
それを、function2やfunction3で文字コードの変換をし、
変換結果が、char_outに入るようになっています。
例えば、構造体A, B, Cの
member1は、EBCDIK→JISに変換、
member2は、バイナリデータなので変換なし、
member3は、JIS→SJISに変換
というような感じです。



No.15445

Re:ポインタの型を場合分けで変える
投稿者---shu(2004/07/12 09:47:31)


>
typedef struct
{
    char member1[1];
    char member2[2];
    char member3[3];
} A;

typedef struct
{
    char member1[5];
    char member2[6];
    char member3[7];
} B;

typedef struct
{
    char member1[1];
    char member2[3];
    char member3[5];
} C;

function(char *, char *, int);

void main()
{
    char char_in[] = "abcdefghijklmnopqr";
    char char_out[20];
    function(char_in, char_out, 1);
}


function(char_in, char_out, 1);
の処理で、char_outにはどういう文字列が入りますか?
最終的にどういう感じの出力にしたいのでしょうか?
文字列から3文字ずつ取ってきてそれをひとつの文字列にするのかな?


No.15482

Re:ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/13 12:56:28)


No.15478を投稿する場所を間違えてしまいました。
本来は、ここにくるべきものです。



No.15476

Re:ポインタの型を場合分けで変える
投稿者---REE(2004/07/13 11:25:35)


>function2やfunction3を呼び出す時の引数をどう書けばよいのでしょう?
>それとも、そもそも、共用体の使い方を勘違いしていますか?

共用体の使い方はあっていると思いますが、今回の場合には、全く役に立ちません。
共用体は、同じメモリ範囲を違う名前や大きさで扱うためのものです。

A,B,Cなどをやめて、下記の様にすることをお勧めします。

typedef struct
{
    int start; 
    int size;   // きっと必要になる
} area;

struct
{
    area member1;   // 配列でも可
    area member2;
    area member3;
} format[] = {{{0, 1}, {1, 2}, { 3, 3}}, 
              {{0, 5}, {5, 6}, {11, 7}},
              {{0, 1}, {1, 3}, { 4, 5}}};



int function(char *inbuf, char *outbuf, int kind)
{
    function2(inbuf + format[kind].member1.start, outbuf + format[kind].member1.start);
    ...
}

こうした場合、function2の引数にメンバのサイズを追加する必要が出てくると思われます。



No.15481

Re:ポインタの型を場合分けで変える
投稿者---ねこのひげ(2004/07/13 12:54:19)


REE様、ありがとうございます。

> A,B,Cなどをやめて、下記の様にすることをお勧めします。
> typedef struct
> ・・・
> こうした場合、function2の引数にメンバのサイズを追加する必要が出てくると思われます。

おお!、これは、とてもすっきりしていて良いですね。
パディングの問題も出ないですし、まとめたかった関数もまとまっていますし。

まだ、文法は理解していても、それを使いこなすことができていなくて、
恥ずかしい限りですが、このようなものをすぐに思いつくようになるように
頑張りたいと思います。
いずれ、また何かお聞きすることが出てくるかもしれませんが、
どうぞよろしくお願い致します。