0

Cでは、私はこのような構造を持っています

typedef struct
{
 char *msg1;
 char *msg2;
 .
 .
 char *msgN;
}some_struct;

some_struct struct1;
some_struct *pstruct1 = &struct1;

インクリメントまたはデクリメントされると、この構造体の次/最後のメンバー変数を与えるポインターまたは変数を保持したいと思います。char * の配列は既にこのように設計されているため、使用したくありません。

ユニオンと構造体の組み合わせを使ってみたのですが、そのコードの書き方がわかりません。

思考イテレータが役立つかもしれませんが、これは C です。何か提案はありますか?

4

3 に答える 3

2

安全にそれを行うことはできません。隣接する文字ポインタが配列内にあるかのように実際に隣接している(パディングなしで)可能性がありますが、それが未定義動作の地雷原にほぼまっすぐ入っているかどうかはわかりません。

それをインデックスに抽象化して、次のようにすることができます。

char * get_pointer(some_struct *p, int index)
{
  if(index == 0)
    return p->msg1;
  if(index == 1)
    return p->msg2;
  /* and so on */
  return NULL;
}

次に、自由にインクリメント/デクリメントできるインデックスを操作し、必要に応じて呼び出すだけget_pointer()でメッセージポインタにマップできます。

于 2012-12-13T09:33:01.190 に答える
2

厳密な C を使用してこれを行うことができますが、標準に確実に準拠するには、特定の予防措置を講じる必要があります。以下で説明しますが、注意すべき点は次のとおりです。

(0) 次の宣言を含めて、パディングがないことを確認します。

extern int CompileTimeAssert[
    sizeof(some_struct) == NumberOfMembers * sizeof(char *) ? 1 : -1];

(1) メンバーのアドレスではなく、構造体のアドレスからポインターを初期化します。

char **p = (char **) (char *) &struct1;

(上記は必要ないと思いますが、C標準からさらに推論を挿入する必要があります。)

++(2) ポインタを使用または追加する代わりに、次の方法でポインタをインクリメントします。

p = (char **) ((char *) p + sizeof(char *));

ここに説明があります。

(0) の宣言は、コンパイル時のアサーションとして機能します。構造体にパディングがない場合、構造体のサイズは、メンバーの数にメンバーのサイズを掛けた値に等しくなります。次に、三項演算子が 1 に評価され、宣言が有効になり、コンパイラが処理を続行します。パディングがある場合、サイズは等しくなく、三項演算子は -1 に評価され、配列は負のサイズを持つことができないため、宣言は無効になります。その後、コンパイラはエラーを報告して終了します。

したがって、この宣言を含むプログラムは、構造体にパディングがない場合にのみコンパイルされます。さらに、宣言はスペースを消費せず (定義されていない配列のみを宣言します)、他の式 (条件が true の場合に配列サイズ 1 に評価される) で繰り返すことができるため、異なるアサーションが発生する可能性があります。同じアレイ名でテストされています。

項目 (1) と (2) は、通常、ポインター演算が配列内でのみ動作することが保証されている (末尾の概念的な番兵要素を含む) という問題に対処しています (C 2011 6.5.6 8 による)。ただし、C 標準では、C 2011 6.3.2.3 7 で、文字型に対して特別な保証が行われます。 . オブジェクトのサイズまで結果を連続的にインクリメントすると、オブジェクトの残りのバイトへのポインタが生成されます。

(1) では、C 2011 6.3.2.3 7 からわかりますが、これ(char *) &struct1は の最初のバイトへのポインターですstruct1。に変換されるとき(char **)、それは の最初のメンバーへのポインターでなければなりませんstruct1(特に、C 2011 6.5.9 6 のおかげで、同じポインターが異なる型であっても同じオブジェクトを指すことが保証されます)。

最後に、(2) は、配列演算がポインターで動作することが直接保証されていないという事実を回避します。つまり、厳密には配列内にないポインターをインクリメントp++するため、6.5.6 8 では算術演算は保証されません。char *それを 4 回繰り返して に戻しchar **ます。パディングがないため、これは次のメンバーへのポインターを生成する必要があります。

(たとえば 4)のサイズを追加することはchar **、 one の 4 つのインクリメントと同じではないと主張する人もいるかもしれませんcharが、確かに、標準の意図は、合理的な方法でオブジェクトのバイトをアドレス指定できるようにすることです。ただし、この批判さえも避けたい場合は、(サイズが 4 の実装では) または(サイズが 8 の場合) に変更+ sizeof(char *)できます。+1+1+1+1+1+1+1+1+1+1+1+1

于 2012-12-13T12:52:28.203 に答える
0

最初のメンバーのアドレスを取得し、次の場所に保存しchar **ます。

char **first = &struct1.msg1;
char **last = &struct1.msg1 + sizeof(some_struct) / sizeof(char *) - 1;

char **ptr = first;  /* *ptr is struct.msg1 */
++ptr;               /* now *ptr is struct1.msg2 */

char *これは、構造にメンバーのみが含まれていることを前提としています。

于 2012-12-13T09:32:21.473 に答える