8

こんにちは、私はCで割り当てに取り組んでおり、未知のタイプのパラメーターを関数に渡す必要があります。

たとえば、次のようなものがあるとします。

int changeCount(void* element)
{
    element.Count = element.Count++;

    return 1;

}

可変要素が無効である理由は、3種類の可能性があるためです。ただし、3つすべてに「Count」という名前のメンバー変数があります。

Eclipeseで記述した実際のコードをコンパイルしようとすると、次のエラーが発生します。

エラー:構造体または共用体ではないもののメンバー'Count'の要求

コンパイラが「要素」のタイプを事前に知らないため、これが起こっていると思います。しかし、なぜこれが機能しないのかわかりません。

手伝ってくれてありがとう!

4

7 に答える 7

12

これらの3つのタイプのいずれかにポインタをキャストしてから使用する必要があります。何かのようなもの:

MyType* p = (MyType*)element;
p->count++;

ただし、オブジェクトを間違ったタイプにキャストすると危険な場合があるため、キャストするオブジェクトのタイプを確認する必要があります。

于 2009-11-21T17:41:12.610 に答える
10

まず、voidへのポインターを渡します。これは、不明なタイプの有効なアプローチですが、さまざまなオブジェクトタイプへのポインターを渡す必要があります。

Cは動的言語ではないため、実行時にシンボリック型の情報が大幅に削除されるため、3つの型すべてにメンバーがあると言うCountと、関数の設計には役立ちません。

アクセスできる唯一の方法は、または(つまり、だけでなく)で参照を解除する前に、パラメーターを正しいポインター・タイプにCountキャストすることです。void*->(*element).Count.

互換性のあるレイアウト(実装に依存する可能性が高い)を持つタイプに依存している場合を除き、関数が実行する正しいキャストを決定するのに役立つものを渡す必要もあります。この時点で、3つの別個の機能とより優れた型安全性を利用したほうがよい場合があります。

于 2009-11-21T17:43:22.327 に答える
6

実際の型にキャストする必要がありますが、同じ場所にcountプロパティがない構造体では、予期しない結果が生じる可能性があります。

typedef struct _A 
{
    int count;
    int x;
}A;
int changeCount(void* element)
{
    ((A*)element)->Count++;

    return 1;

}

また、次のような構造体へのポインタを渡すと、カウントではなくxフィールドがインクリメントされることにも注意してください。

typedef struct _B 
{
    int x;      
    int count;

}B;

    B st;
    B* pst = &st;
    changeCount(pst);
于 2009-11-21T17:44:01.873 に答える
3

.Count要素がこれら3つのタイプのそれぞれで同じタイプである場合は、マクロを使用することもできます。それがintであると仮定すると、これを行うことができます:

#define changeCount(a) _changeCount((a), &(a)->Count)

int _changeCount(void* element, int *count)
{
  (*count)++;
  return 1;
}

これはa.Count機能します。これは、関数を呼び出した後ではなく、関数を呼び出したときにアドレスが解決されるためです(タイプがわからなくなったとき)。ただし、関数を呼び出すときは、適切な型を持っていると思います。だからSometype x; changeCount(x);うまくいくでしょうが、すでにあるものを渡すこと(void*)はできません。

また、あなたの元の表現element.Count = element.Count++;はかなり奇妙です。インクリメントする場合は、、element.Count++またはを使用しますelement.Count = element.Count + 1

于 2009-11-21T17:52:08.330 に答える
1

キャストを使用します。

ClassName(element).count++

また、element.Count = element.Count ++; 行は冗長です。必要なのはelement.count++(値を1つ増やす)だけです。

于 2009-11-21T17:46:41.493 に答える
1

コンパイル時に、Cはほとんどの型情報を破棄し[1]、オフセットのみを残します。したがって、関数は疑似コードで次のようにコンパイルされます。


changeCount:
  assign *(*(stack_ptr + offset_element) + offset_Count) + 1
    to *(stack_ptr + offset_element) + offset_Count;
  assign 1 to return_value;
  pop

stack_ptrは、changeCountを呼び出したときに作成されたスタックフレームの場所です。offset_elementは、stack_ptrを基準にした要素引数の場所ですが、offset_Countとは何ですか。コンパイラがコードについて知っているのは、例で示したものだけであることに注意してください。elementはジェネリックポインタであり、実際には何かへのポインタではありません。変数[2]にキャストまたは割り当てることにより、どの要素が指しているかをコンパイラーに通知する必要があります。


typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
    counted_t* counted = element;
    counted.Count++;
    return 1;
}

この関数は、基本的に上記と同じ(擬似)コードを生成しますが、コンパイラーは、Countのオフセットがどうあるべきかを認識します。

あなたは、どの要素が指しているのかというタイプには3つの可能性があると述べています。これを処理する方法はいくつかあります。区別された結合または「継承された」構造のいずれかです。区別されたユニオンの使用については、たとえば、1つの要素が3つの可能性のどれを識別する列挙型であり、別の要素が3つの可能な構造のユニオンである構造。これは、ML言語(OCaml、Haskellなど)が代数的データ型と呼んでいるもの、またはPascalのユニオンとほぼ同じです。「継承」には、タイプ定義を使用できます。


typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;

この場合、上記のchangeCount関数を使用して、counted_int_t、counted_double_t、またはcounted_charptr_tへのポインターを渡すことができます。

何が起こるかというと、counted_t要素が最初である限り、コンパイラは同じ場所の「子孫」構造にCount要素を持つ3つの構造をレイアウトします。(少なくとも、これまでに使用したすべてのコンパイラーと、これまでに見たすべてのコードで、これはある時点でC標準になったと思いますが、これはごく普通のイディオムです。)

[1]デバッグ情報を除いて、コンパイラーにそれを発行するように指示した場合。ただし、プログラムはその情報にアクセスできないため、この場合は役に立ちません。

[2] x ++(postincrement)操作は、適用される変数(まあ、左辺値)をインクリメントします。元のコードでの割り当ては不要です。

于 2009-11-21T19:02:56.487 に答える
0

これはまさに問題です:

コンパイラが「要素」のタイプを事前に知らないため、これが起こっていると思います。しかし、なぜこれが機能しないのかわかりません。

メソッドまたはメンバーデータを名前で呼び出すことは、通常、静的に型指定された言語の機能ではありません。

ポインタが実際にその型へのポインタである場合、aでさえ、 Tが型でvoid *あるaにのみ確実にキャストできます。T *

C ++では、3つのタイプすべてが、メソッドCount(つまり、インターフェイスICountable)を持つ同じ仮想ベースクラスXから継承する可能性があります。次に、にキャストしX *て使用しp->Countます。これは慣用的なC++になります。すべてのクラスが同じインターフェイスを実装しているため、すべてこのメソッドをサポートしています。これは、Tommy McGuireの回答に示されているのと同じ構造体オフセットのトリックに依存することに似た、言語でサポートされる方法であり、構造体はすべて慣例により類似しています。構造体を変更する場合、またはコンパイラーが構造体をレイアウトするための標準から逸脱する場合は、お湯に浸かることになります。

メソッドは非常に単純なので、通常は関数でラップすることはなく、単にインラインと呼ぶだけなので、これはかなりトイプロブレムだと思わずにはいられません T t; t.Count++;

于 2009-11-21T17:49:25.713 に答える