3

私はこの膨大なコードのリポジトリを扱っていて、構造体の 1 つに重要なフィールドが欠けていることに気付きました。コード (構造体を使用する) をできる限り詳しく調べたところ、余分なフィールドを追加してもコードが壊れることはないと結論付けました。

私が台無しにした可能性がある場所についてのアイデアはありますか?

また、設計に関するアドバイスを歓迎します - これを達成するための最良の方法は何ですか?

(私が明確でない場合):

typedef struct foo
{
  int a;
  int b;
}
foo;

今は次のとおりです。

typedef struct foo
{
  int a;
  int b;
  int c;
}
foo;
4

10 に答える 10

6

その構造がどこかでシリアル化/逆シリアル化されている場合は、コードのそのセクションに注意してください。

メモリが割り当てられているコードの領域を再確認します。

于 2010-01-21T15:16:33.837 に答える
4

sizeof(struct) を使用してすべての場所にメモリを割り当て、 -> または . オペレーターの皆さん、問題に直面する必要はないと思います。ただし、メンバーを追加しようとしている場所にも依存します。注意しないと、構造の配置が台無しになる可能性があります。

于 2010-01-21T15:13:48.363 に答える
4

あなたが上に書いたことから、私は何も間違っているとは思いません。私が考えることができる2つのこと:

  1. コードを変更して再コンパイルするたびに、「隠れた」バグを見つける機能が導入されます。つまり、新しいデータ構造が破損するのに十分な大きさの初期化されていないポインターです。
  2. c使い始める前に必ず初期化していますか?

ファローアップ:

あなたはまだエラーを見つけていないので、構造体を見るのをやめます。誰かが書いたことがありますが、最初に馬を探し、次にシマウマを探します。つまり、エラーはおそらく特殊なものではありません。単体テストのカバレッジはどの程度ですか? これは、ほぼ常に 0% を意味するレガシー コードであるか、少なくともそれが私の経験であると想定しています。これは正確ですか?

于 2010-01-21T15:16:05.823 に答える
2

私が台無しにした可能性がある場所についてのアイデアはありますか?

何もない。すべての。それはすべて、これが使用される方法、場所、および理由に依存します。

あなたが話しているこの構造がCスタイルのPODであり、コードが最も単純であると仮定すると、それでうまくいくでしょう。しかし、より野心的なことをしようとしている瞬間、(オブジェクトを作成する方法と場所に応じて) アラインメントの問題に対処し、少なくともパディングを行います。これが C++ で、POD にカスタム オペレータ/ctors などが含まれている場合、多くの問題が発生します。エンディアンに依存している場合など、クロスプラットフォームの問題が発生する可能性があります。

于 2010-01-21T15:15:26.807 に答える
2

コードに堅牢な単体テストのセットがあれば、おそらく問題を突き止めるのがはるかに簡単になるでしょう (設計のアドバイスを求めました ;))

この巨大なコードベースのどこでも新しい「c」変数を使用する必要はないと思います。追加または変更しているコードで使用できるように追加しているだけですか? foo に c を追加する代わりに、foo オブジェクトと c を含む新しい構造体 bar を作成できます。次に、必要な場所で bar を使用します。

実際のバグに関しては、情報がほとんどないものであれば何でもかまいませんが、推測する必要がある場合は、誰かが sizeof() の代わりにマジック ナンバーをどこかで使用したと思います。

于 2010-01-21T21:03:59.820 に答える
1

質問にC++のタグを付けたので、次のようになります。

将来的には、Pimpl / d-Pointerは、互換性を損なうことなく、クラスを拡張または再設計する際の自由度を大幅に高める戦略です。

たとえば、あなたが最初に書いた場合

// foo.h
class Foo {
public:
    Foo();
    Foo(const Foo &);
    ~Foo();
    int a() const;
    void a(int);
    int b() const;
    void b(int);
private:
    class FooPrivate *const d;
};

// foo.c
class FooPrivate {
public:
    FooPrivate() : a(0), b(0) {}
    FooPrivate(const FooPrivate &o) : a(o.a), b(o.b) {}
    int a;
    int b;
};
Foo::Foo() : d(new FooPrivate()) {}
Foo::Foo(const Foo &o) : d(new FooPrivate(*o->d)) {}
Foo::~Foo() { delete d; }
int Foo::a() const { return d->a; }
void Foo::a(int a) { d->a = a; }
// ...

これを簡単に拡張できます

// foo.h
class Foo {
public:
    // ...
    int a() const;
    void a(int);
    int b() const;
    void b(int);
    int c() const;
    void c(int);
    // ...
};

// foo.c
class FooPrivate {
    // ...
    int a;
    int b;
    int c;
};
// ...

を使用して既存の(コンパイルされた!)コードを壊すことなくFoo

于 2010-01-21T18:59:35.523 に答える
1

を探しmemcpy, memset, memcmpます。これらの関数はメンバー単位ではありません。以前の構造体の長さを使用して使用された場合、問題が発生する可能性があります。

のすべてのインスタンスについてもファイルを検索しますstruct。新しい重要なフィールドを使用しない関数またはメソッドがある場合があります。#define他の人が言ったように、またはで構造を見つけた場合は、typedefそれらも検索する必要があります。

于 2010-01-21T17:41:44.973 に答える
0

最初のメンバーが何かを壊す以外の場所に構造体メンバーを追加すると、コードの動作が未定義になり、間違っています。したがって、少なくとも他の誰か(または以前の自分)が破損の責任を負っています。しかし、はい、未定義の動作には「私たちがやりたいことをすることが起こる」ことが含まれるので、他の人が言うように、メモリ割り当て、シリアル化(ネットワークとファイルIO)に注意してください。

余談ですが、typedef FOO ... struct FOOを見ると、CコードをC++のように見せようとしているように見えます。私はここで少数派にいることに気づきました:)

于 2010-01-21T16:31:05.510 に答える
0

C 構造体の最後に新しい要素を追加しても常に安全です。その構造体が異なるプロセスに渡された場合のイベント。再コンパイルされたコードは新しい構造体メンバーを認識し、再コンパイルされていないコードは古い構造体のサイズを認識し、認識している古いメンバーを読み取るだけです。ここでの注意点は、新しいメンバーは構造の途中ではなく、構造の最後に追加する必要があるということです。

于 2010-01-22T03:01:03.760 に答える
0

コードを使用してネットワーク経由でデータを転送すると、問題が発生する可能性があります。

于 2010-01-21T15:27:33.730 に答える