Добавление поля в структуру без нарушения существующего кода - PullRequest
3 голосов
/ 21 января 2010

Итак, я работаю с этим огромным хранилищем кода и понял, что в одной из структур отсутствует важное поле. Я посмотрел на код (который использует структуру) как можно точнее и пришел к выводу, что добавление дополнительного поля не нарушит его.

Есть идеи, где я мог облажаться?

Также: приветствуется совет по дизайну - как лучше всего это сделать?

Например, (если я не понял):

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

Теперь это:

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

Ответы [ 10 ]

6 голосов
/ 21 января 2010

Если эта структура где-либо сериализуется / десериализуется, обязательно обратите внимание на этот раздел кода.

Дважды проверьте области кода, в которых выделяется память.

4 голосов
/ 21 января 2010

Из того, что вы написали выше, я не вижу ничего плохого. Две вещи, о которых я могу думать:

  1. Всякий раз, когда вы меняете код и перекомпилируете, вы вводите возможность находить «скрытые» ошибки. То есть неинициализированные указатели, которые могут быть достаточно большими, чтобы их новая структура данных могла быть повреждена.
  2. Вы уверены, что инициализируете c, прежде чем он будет использован?

Follow Up:

Поскольку вы еще не нашли ошибку, я перестану смотреть на вашу структуру. Кто-то когда-то писал, искать лошадей сначала, зебр - вторых. То есть ошибка, вероятно, не экзотическая. Какой охват у вас в ваших юнит-тестах? Я предполагаю, что это устаревший код, который почти всегда означает 0% или, по крайней мере, это был мой опыт. Это точно?

4 голосов
/ 21 января 2010

Если вы используете sizeof (struct) для выделения памяти во всех местах и ​​обращаетесь к членам, используя -> или. операторы, я не думаю, что вы должны столкнуться с какой-либо проблемой. Но это также зависит от того, где вы пытаетесь добавить элемент, это может испортить выравнивание структуры, если вы не будете осторожны.

2 голосов
/ 21 января 2010

Есть идеи, где я мог облажаться?

Ничего. Все. Все зависит от того, как, где и почему это используется.

Предполагая, что эта структура, о которой вы говорите, представляет собой POD в стиле C, а код простейший, вам это сойдет с рук. Но в тот момент, когда вы пробуете что-то более амбициозное, вы сталкиваетесь с проблемами выравнивания (в зависимости от того, как и где вы создаете объекты) и, как минимум, с отступами. Если это C ++ и ваш POD содержит пользовательские операторы / ctors и т. Д. - у вас много проблем. Могут возникнуть кроссплатформенные проблемы, если вы когда-либо полагаетесь на порядок байтов и т. Д.

1 голос
/ 22 января 2010

Если бы в коде был надежный набор модульных тестов, вероятно, было бы намного проще отследить проблему (вы попросили совета по проектированию;))

Полагаю, вам не нужно использовать новую переменную 'c' везде в этой гигантской кодовой базе, вы просто добавляете ее, чтобы использовать ее в каком-то коде, который вы добавляете или модифицируете? Вместо добавления c в foo, вы можете создать новую структуру bar, содержащую объект foo и c. Тогда используйте бар, где это необходимо.

Что касается самой ошибки, то это может быть что угодно с таким небольшим количеством информации, но если бы мне пришлось угадывать, я бы сказал, что кто-то использовал магическое число вместо sizeof () где-то.

1 голос
/ 21 января 2010

Поскольку вы отметили свой вопрос 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.

1 голос
/ 21 января 2010

Ищите memcpy, memset, memcmp. Эти функции не являются членами. Если они были использованы с использованием предыдущей длины структуры, у вас могут возникнуть проблемы.

Также ищите файлы для каждого экземпляра struct. Могут быть функции или методы, которые не используют новое важное поле. Как уже говорили другие, если вы найдете структуру в #define или typedef, вам придется искать их тоже.

0 голосов
/ 22 января 2010

Всегда безопасно добавлять новые элементы в конце структуры C. Событие, если эта структура передается различным процессам. Код, который был перекомпилирован, увидит новый член структуры, а код, который не был, просто будет знать о старом размере структуры и просто прочитает старые члены, о которых он знает. Предостережение заключается в том, что новый элемент должен быть добавлен в конце структуры, а не в середине.

0 голосов
/ 21 января 2010

Если добавление элемента структуры где-либо, кроме первого элемента, что-либо нарушает, то код имеет неопределенное поведение, и это неправильно. Так что, по крайней мере, у вас есть кто-то другой (или ваш прежний я), чтобы обвинить в поломке. Но да, неопределенное поведение включает «случается делать то, что нам хотелось бы», так что, как говорят другие парни, следите за распределением памяти, сериализацией (сетевой и файловый ввод-вывод).

Кроме того, я всегда съеживаюсь, когда вижу typedef FOO ... struct FOO, как будто кто-то пытается заставить C-код выглядеть как C ++. Я понимаю, что я в меньшинстве здесь:)

0 голосов
/ 21 января 2010

Если код используется для передачи данных по сети, это может привести к поломке.

...