0

メインコードからポインターを取得し、ポインターアドレスで変数を操作する汎用関数が必要なコードがあります。問題は、ポインターが char、int、および short 型であることです。フラグなしでそれを実行できるようにしたい、または渡されたポインターのタイプを追跡したいなど3つのうち)

以下の種類は、char ポインターを除いて機能します。これを行うより良い方法はありますか?

       #include <stdio.h>

    void pointerfunction(int *p);
    int a=10;
    short b=20;
    char f=4;
    typedef union 
    {
    int *ptr1;
    short *ptr2;
    char *ptr3;
    }pointers;

    int main()
    {
    pointers mypointers;

    mypointers.ptr1=&a;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr1));
    mypointers.ptr2=&b;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr2));
    mypointers.ptr3=&f;
    pointerfunction(mypointers.ptr1);
    printf("%d\n", *(mypointers.ptr3));
    }


    void pointerfunction(int *p)
    {
    *p=*p*10;  
    }
4

6 に答える 6

3

ユニオンを使用するというあなたの考えは良いものですが、それが実際にどのような種類のポインターであるかを示すために、ユニオンに追加のメンバーが必要になります。

同じマシン上のすべてのポインターは、int *、char *、またはvoid *のいずれであるかに関係なく、同じサイズです。

intとshortが機能する理由は、コンパイラがintとshortをintに変換するため、printf()関数は基本的に両方を同じように認識します。

まず、可能な実装について説明します。ただし、この特定の実装は醜く、実際にはこれを行う方法ではありません。コマンドスイッチを使用していることや、結合を実際に減らして結合を増やしていることなど、多くの問題があるためです。

最初の試みは次のようになります。

#define  POINTER_UNION_TYPE_CHAR   1
#define  POINTER_UNION_TYPE_INT    2
#define  POINTER_UNION_TYPE_SHORT  3

typedef struct {
  int   iType;
  union {
    char *pChar;
    int  *pInt;
    short *pShort;
  } u;
} Pointers;

この構造体を使用すると、次のようになります。

int iValue = 1;
Pointers  PointerThing;

PointerThing.u.pInt = &iValue;  PointerThing.iType = POINTER_UNION_TYPE_INT;

次に、これを使用する関数で、次のようなことを行います。

void pointer_funct (Pointers *pPointers)
{
   switch (pPointers->iType) {
      case  POINTER_UNION_TYPE_CHAR:
           // do things with char pointer pPointers->u.pChar
           break;
      case  POINTER_UNION_TYPE_INT:
           // do things with char pointer pPointers->u.pInt
           break;
      case  POINTER_UNION_TYPE_SHORT:
           // do things with char pointer pPointers->u.pShort
           break;
      default:
           break;
    }
}

これを行うためのより良い方法は、すべてが1つの関数に結合されていることを実行する個別の関数を用意することです。つまり、それぞれが特定のポインター型を処理する3つの異なる関数があります。そうすれば、タイプが何であるかを知っている機能は、先に進んで適切な関数を呼び出すことができます。

これに対する別のアプローチは、これでいくつかのオブジェクト指向技術を使用することです。同様の質問ですが、別の投稿を参照してください。

于 2012-08-15T21:46:10.783 に答える
2

他の人が言ったように、これは C では不可能ですint。3 つのタイプの中で最大のものは正しいですが、この事実の意味を見逃しているようです。

Cでこれが不可能なのはなぜですか?

C では、データはメタデータのオーバーヘッドなしでメモリに直接格納されます。変数は、メモリ内のデータに直接マップされます。作成しない限り(フラグがないという要件に違反するか、渡されるポインターのタイプを追跡する必要があります)、次のような変数に情報は保存されません。

  • それはどのタイプですか
  • 変数が初期化されているかどうか
  • 変数がスコープ内にあるかどうか
  • または (配列/文字列の場合) 使用される長さ、または使用可能なサイズ

他の言語にもあるように。代わりに、この情報は、この情報structを格納する を作成するか、何が起こっているかをプログラマーに覚えてもらうことにより、プログラマーが維持する必要があります。

C はシステム プログラミング言語であり、Java や C# のようなオーバーヘッドがないため、システム プログラミングに適しています。

わかりましたが、ユニオンでは機能しないのはなぜですか?

指しているタイプのさまざまなサイズの意味は何ですか? 次のメモリ ダイアグラムを検討してください。各文字は 4 ビット、int は 32 ビット、short は 16 ビット、char は 8 ビットです。

ニブル:89ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
[other ][data ][int ][int ][int ][mo][re][ ][da][ta] // Ints
[other ][data ][sh][or][t ][sh][or][t ][mo][re][ ][da][ta] // Shorts
[other ][data ][][][][][][][][][][][][][mo][re][ ][da][ta] // Chars

これはアライメントとエンディアンの問題を完全に無視していることに注意してください。いくつかのプラットフォーム (ARM を含む。これは、他の質問のいくつかで見られます) があり、あなたを助けることができるアライメントについて特定の保証が行われています.(†)

ただし、静的メモリまたはヒープ上のメモリにはまだ問題が残っています。文字配列に文字列を格納するとどうなるかを考えてみましょうABCDEFGHIJKL。ASCIIAが 0x41 であることを思い出すと、メモリ内では次のようになります。

[その他][データ]4142434445464748494A4B4C[も][れ][][だ][た]

Cこれを整数として逆参照する関数にポインタを渡したとします。

                    [int ] // C への Int ポインター
[other ][data ][][][][][][][][][][][][][mo][re][ ][da][ta] // Chars
[その他][データ]4142434445464748494A4B4C[も][れ][][だ][た]
                    ^-- C はここにあります。0x43

ここで int ポインターを使用すると、C 仕様に違反します。

それだけでは不十分で、コンパイラが論理的に動作すると仮定すると、ワード境界を越えてメモリを逆参照しようとし、バス フォールトまたは使用法フォールトをスローする可能性があります (ARMv7 で実際に何をするか忘れましたが、これらのフォールトのいずれかがプログラムを終了します)。

それでも十分ではなく、何らかの形で要求どおりに動作する場合、0x43 ではなく 0x43444546 の値を使用しているため、操作は間違った応答を生成します。


ARM プロセッサでのメモリ アラインメントに関するいくつかの脚注

(†) たとえば ARM では、ABI は通常の使用ではスタックをワード境界で整列する必要があると指定しています ( sp % 4 == 0)。

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
[その他][データ][int][int][int][モ][レ][][ダ][タ]
[その他][データ][sh][or][t][sh][or][t][mo][re] ...
[その他][データ][][][][][][][][] ...

スタックは、パブリック インターフェイスに対してダブルワード アラインメントも保証されており、内部的に維持する必要はありません。詳細については、AAPCS の 5.2.1 を参照してください。それにもかかわらず、これはあなたが依存したいものではなく (ほとんどの場合、移植可能なコードが望ましいです)、コンパイラまたは生のアセンブリ コードを作成している場合を除き、知る必要さえありません。

于 2012-08-15T22:31:05.653 に答える
1

これは悪い考えです。ここを参照してください:

void pointerfunction(int *p);
short b=20;
short c=20;
typedef union
{
  int *ptr1;
  short *ptr2;
  char *ptr3;
} pointers;

int main()
{
  pointers mypointers;

  mypointers.ptr2=&b;
  pointerfunction(mypointers.ptr2);
  printf("b=%d,c=%d\n", b,c);

  mypointers.ptr2=&c;
  pointerfunction(mypointers.ptr2);
  printf("b=%d,c=%d\n", b,c);

}


void pointerfunction(int *p)
{
  *p=*p*10;
}

これは、私のシステムでは、次のように出力します。

b=200,c=200
b=200,c=2000

それはあなたがしたかったことですか?

于 2012-08-15T21:30:58.063 に答える
1

テンプレート関数のようなものが欲しいようです。ただし、C はテンプレート関数または関数のオーバーロードをサポートしていません。

3 つの型のサイズが異なるため、ポインターから型を推測できます。したがって、マクロを使用して、オーバーロードされた関数の感触を作成できます。

#define pointerfunction(x) do { \
    switch (sizeof(*x)) { \
    case sizeof(int):   pointerfunction_int((void *)x);   break; \
    case sizeof(short): pointerfunction_short((void *)x); break; \
    case sizeof(char):  pointerfunction_char((void *)x);  break; \
    default:            fprintf(stderr, "unknown pointer type for %p\n", x); \
                        break; \
    } \
} while (0)

#define pointerfunction_template(T) \
    void pointerfunction_ ## T (T *x) { *x = *x * 10; }

pointerfunction_template(int);
pointerfunction_template(short);
pointerfunction_template(char);

次に、次のようにマクロを使用できます。

int a=10;
short b=20;
char f=4;

int main () {
    pointerfunction(&a);
    pointerfunction(&b);
    pointerfunction(&f);
    return 0;
}

ただし、この手法は一般的には機能しません。特に、2 つの型が同じサイズの場合は失敗します。次に、型自体をマクロ呼び出しに埋め込む必要があります。

#define pointerfunction_call(T, x) pointerfunction_ ## T(x)

pointerfunction_template(float);
pointerfunction_template(double);

float g = 2.2;
double h = 3.1;

pointerfunction_call(float, &g);
pointerfunction_call(double, &h);
于 2012-08-16T06:29:02.353 に答える
0

これは機能しません。ユニオンに格納しているポインタのタイプを知る方法はありません。

ユニオンをメンバーとして持つ構造体と、使用している値の種類を示す別のフィールドを定義できます。ポインタのサイズは、それが何を指していても同じであることを忘れないでください。私はこれをテストしていませんが、あなたはその考えを理解しています。

#include <stdio.h>

int a=10;
short b=20;
theChar f=4;

#define INT 1
#define SHORT 2
#define CHAR 4

typedef union 
{
int *ptr1;
short *ptr2;
char *ptr3;
}pointers;

typedef struct {
    pointers ps;
    type int;
} myStruct;


void pointerfunction(myStruct theStruct)
{
    switch (theStruct.type) {
        case INT:
            *(theStruct.ptr1) += 10;
            break;
        case SHORT:
            *(theStruct.ptr2) += 20;
            break;
        case CHAR:
            *(theStruct.ptr3) += 30;
            break;
    }
}


int main()
{
    // Example with a char
    myStruct theStruct;
    theStruct.type = CHAR;

    theStruct.ptr3=&theChar;

    pointerfunction(theStruct);
    printf("%d\n", *(theStruct.pointers.ptr3));
}
于 2012-08-15T21:30:30.960 に答える
0

Cは「一般的な」関数を実行できません。少なくとも、C++ テンプレートと関数のオーバーロードで得られる「意味を実行する」という意味ではできません。

template <typename T>
void pointerfunction(T *p)
{
  *p = *p * 10;
}
...
pointerfunction(&a);
pointerfunction(&b);
pointerfunction(&c);

ある時点で、関数適切に機能するためには、ポインターの正しい型が何であるかを知る必要があります。ポインター型をキャストするint *と、非整数型で問題が発生します。

できることの1 つは、各タイプのポインターを処理する別の関数を作成することですが、次のように、共通の「ジェネリック」インターフェイスを介してそれらの関数を呼び出します。

void intfunc(void *p)   { int   *lp = *p; *lp *= 10; }
void charfunc(void *p)  { char  *lp = *p; *lp *= 10; }
void shortfunc(void *p) { short *lp = *p; *lp *= 10; }

void callfunc(void *data, void (*ptrfunc)(void *))
{
  ptrfunc(data);
}
...
callfunc(&a, intfunc);
callfunc(&b, shortfunc);
callfunc(&c, charfunc);

これは見栄えがよくなく、C++ テンプレートとは異なり、タイプ セーフのふりを窓の外に放り出します ( void *.

しかし...

このアプローチは非常に柔軟であり、機能させるためにあまりにも不自然なことを する必要はありません。

于 2012-08-15T22:18:27.917 に答える