4

さまざまなタイプの構造体を引数として受け入れる関数が必要です。したがって、特定の型がないため、void* を使用する必要があります。問題は、この関数に構造体を渡すとき、関数内のこの構造体の既知のメンバーにアクセスするにはどうすればよいかということです。具体的には、すべての構造体がメンバーとして str1 を持っていることを知っており、たとえば、それを印刷したいと考えています。サンプルコードは次のとおりです。

struct {  
  char* str1;  
  float tt1; } var1 = {"This is me", 12};  

struct {  
   char* str1;  
   int   tt2; } var2 = {"This is you", 18};  

void printStruct(void* str)  
{  
   printf("\n the structure string is %s", ??);   
  //can I put something in ?? to print the string?  
}

main(....)  
{  
   printStruct(&var1);  
   printStruct(&var2);  
}
4

6 に答える 6

7

関数ポインタを渡して、関数をそのユーザーから切り離すことができます

void printStruct(void* data, char * (*namef)(void *data)) {  
   printf("\n the structure string is %s", namef(data));    
}

次にstruct S1*、関数に a を渡すと、次のように getter を渡すことができます。

char * whois(void *data) {
  return ((struct S1*)data)->str1;
}

int main(void) {
  struct S1 s = {"This is me", 12};
  printStruct(&s, whois);
}

offsetofただし、より醜い代替手段を使用できます

void printStruct(void* data, size_t named) {  
   printf("\n the structure string is %s", *(char**) ((char*)data + named));    
}

int main(void) {
  struct S1 s = {"This is me", 12};
  printStruct(&s, offsetof(struct S1, str1));
}

offsetof構造体内のメンバーのオフセットを与えるマクロです。関数では、ポインターをメンバーに配置し(適切な型を取得するにはchar*キャストする必要があります)、メンバー値を取得します。char**

ただし、関数を使用する最初の方法を好むでしょう。dataそうすれば、print 関数はそれが構造体を指していることを知る必要さえありません。これをブラック ボックスとして getter 関数に渡すことができ、その中でコンパイラに低レベルのオフセット計算を処理させることができます。

于 2010-04-09T20:23:33.200 に答える
6

必要な型にキャストしてから、メンバーにアクセスできます。たとえば、プロトタイプがあると仮定して、次のようにします。

struct X
{
char* str1;  
float tt1; } var1 = {"This is me", 12};  

void printStruct(void* str)
{
    ((struct X *)str)->str1;
    ...
}

ブラケットの使用に注意してください。void* 型から struct var2 * へのキャストが行われ、この型 (最も外側の () ブラケット内) は、その型の変数としてアクセスできます。

于 2010-04-09T20:15:14.587 に答える
4

構造体に名前を付ける必要があります。例えば、

struct foo {
   char * str1;
   float tt1;
};

次に、次のようにインスタンスを宣言できます。

struct foo var1 = { "This is me", 12 };

次に、関数でvoid *aにキャストできます。struct foo *

void printStruct(void * ptr) {
   printf("str=%s\n", ((struct foo *)ptr)->str1);
}

そして、次のように呼び出すことができます:

int main(...) {
   struct foo var1 = { "This is me", 12 };
   printStruct(&var1);
}

更新:コメンター Johannes は正しかったです。私の答えは、元の投稿者が各構造体の型を知らないかもしれないと言ったという事実を説明していません。更新されたソリューションは以下のとおりです。

すべての構造体に が含まれていることがわかっているので、構造char * str1体を のみを含む汎用のタグ付き構造体に直接キャストすることで、C の「フラット メモリ モデル」を利用できますchar * str1これは、 が structs最初の要素である場合にのみ機能char * str1します。

struct base { char * str1; };
struct { char * str1; float tt1; } var1 = { "This is me", 12 };
struct { char * str1, int   tt2; } var2 = { "This is me", 18 };

void printStruct(void * ptr) {
   printf("str is %s\n", ((struct base *)ptr)->str1);
}

ただし、これはかなり汚いハックです。共有したい他のメンバーがいる場合、この同じトリックを使用することはできません。

もう 1 つのオプションはstruct、共用体で実際に使用されている型を追跡するために、列挙子を使用して共用体を使用する を定義することです。

enum { FLOAT_TYPE, INT_TYPE };
struct foo {
   char * str1;
   union {
      float tt1;
      int tt2;
   } numericValue;
   int unionType;
};

これにより、次のように変数を定義できます。

struct foo var1 = { .str1 = "This is me", .numericValue.tt1 =12, .unionType = FLOAT_TYPE };
struct foo var2 = { .str1 = "This is me", .numericValue.tt2 =18, .unionType = INT_TYPE };

struct foo *次に、さまざまなタイプの構造体を処理する必要はありません。代わりにvoid ポインターをキャストできます。

void printStruct(void * ptr) {
   struct foo * p = (struct foo *)ptr;
   printf("string is %s\n", p->str1);
}

これにはもう少し作業が必要になるかもしれませんが、IMO の方がはるかにクリーンです。

于 2010-04-09T20:20:37.600 に答える
2

ほとんどのコンパイラでは、メンバーをそのようにキャストしてアクセスしても安全です。これは、両方の構造体の最初のメンバーであるため、アラインメントが同じになる可能性があります。

ただし、ポインターを構造体にキャストする必要があるため、タグが必要です。

struct S1 { char* str1; float tt1; } var1 = {"This is me", 12};

struct S2 { char* str1; int tt2; } var2 = {"This is you", 18};

void printStruct(void* str) { printf("\n the structure string is %s", ((struct S1 *)str)->str1); }

ただし、変数が多く、順序が異なる、より複雑な例では、これは安全ではない可能性があることに注意してください。すべては、コンパイラが選択するアラインメントに依存します。

于 2010-04-09T20:17:57.313 に答える
2

まず、簡単な例:

int booger(int type, void * st) {
   switch(type) {
      case foo:
        struct foo * f = (struct foo *)st;
        /// do stuff
        break;
      case bar:
      /// ...

次に、構造体について: 最初のメンバー (文字列) のみを含む新しい構造体を定義します: struct st { char * str; }; これで、他のすべての構造体を virtual_st * にキャストして関数に渡し、最初の例と同じことを行って実際のメンバーを取得できます。

int baz(struct st * s) {
    if (!strcmp(s->str, "foo") ) {
       // ...

編集: ちなみに、これは c を出力する c++ コンパイラが行う方法と似ていますが、初期フィールドがメンバー関数と静的クラスメンバーの関数ポインターを含む型構造へのポインターである点が異なります。ただし、多重継承がどのように機能するかは正確にはわかりません。

于 2010-04-09T20:18:49.713 に答える
0

を渡している場合void*、それが何かへのアドレスであることを除いて、メソッドが渡しているものについて何かを知る方法がわかりません。これが意味することは、それを使用する前にキャストする必要があるということです。さもなければ、struct渡すすべての がchar*毎回まったく同じ場所にあることを確認してください。

于 2010-04-09T20:17:45.570 に答える