Правильный способ инициализации структур C ++


82

Наш код включает структуру POD (Plain Old Datastructure) (это базовая структура C ++, в которой есть другие структуры и переменные POD, которые необходимо инициализировать вначале).

Судя по тому, что я читал , кажется, что:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

должен инициализировать все значения до нуля, как это делает:

myStruct = new MyStruct();

Однако, когда структура инициализируется вторым способом, Valgrind позже жалуется, что "условный переход или перемещение зависят от неинициализированного значения (значений)" при использовании этих переменных. Мое понимание здесь ошибочно, или Valgrind выдает ложные срабатывания?


1
также обратите внимание, что new MyStruct()не требуется устанавливать какие-либо байты заполнения в C ++ 03. В C ++ 0x это так. Любые биты заполнения будут установлены в 0 в C ++ 0x.
Йоханнес Шауб - литб

Спасибо за улов, отредактировал свой вопрос.
Shadow503


Насколько я понимаю, вы не должны получать это сообщение об ошибке. Возможно, ваша структура данных не является POD? Можете ли вы сократить свой код до самой маленькой программы, которая все еще демонстрирует это поведение, и опубликовать эту дистиллированную программу? (См. Sscce.org ).
Robᵩ

Какой компилятор? Вы можете опубликовать определение MyStruct?
Алан Стоукс,

Ответы:


121

В C ++ классы / структуры идентичны (с точки зрения инициализации).

Структура, отличная от POD, также может иметь конструктор, чтобы она могла инициализировать элементы.
Если ваша структура является POD, вы можете использовать инициализатор.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

В качестве альтернативы вы можете использовать конструктор по умолчанию.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Я считаю, что valgrind жалуется, потому что именно так работал C ++. (Я не совсем уверен, когда C ++ был обновлен с конструкцией по умолчанию с нулевой инициализацией). Лучше всего добавить конструктор, который инициализирует объект (структуры разрешены конструкторами).

В качестве примечания:
многие новички пытаются оценить init:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

Быстрый поиск по «самому неприятному синтаксическому анализу» даст лучшее объяснение, чем я.


1
У вас есть справочник по этой нулевой инициализации переменных в C ++? Последнее, что я слышал, он по-прежнему ведет себя так же, как C. IIRC, компилятор MSVC в какой-то момент даже отбросил неинициализированные элементы с нулевой инициализацией (я полагаю, из соображений производительности), что, по-видимому, создало довольно серьезные проблемы для других подразделений MS :)
Марк Мутц - mmutz

1
нет, C()но похоже ты прав .
Марк Мутц - mmutz

4
С класса POD, T, T()всегда равна нулю инициализирован скалярные подобъектов. Имя инициализации верхнего уровня в C ++ 98 называлось иначе, чем в C ++ 03 («инициализация по умолчанию» и «инициализация значения»), но конечный эффект для POD такой же. И в C ++ 98, и в C ++ 03 все значения будут равны нулю.
Йоханнес Шауб - лит

1
@RockyRaccoon, C c();к сожалению, не является объявлением переменной, а скорее предварительным объявлением функции (функция с именем 'c', которая не принимает параметров и возвращает объект типа C). Google: Самый неприятный синтаксический анализ
Мартин Йорк,

1
@RockyRaccoon Разница здесь и в вопросе, на который вы ссылаетесь в своем комментарии; в том, что в этом случае в конструкторе не используются параметры. Таким образом, компилятору трудно проанализировать это и определить разницу между объявлением переменной и предварительным объявлением функции. Он выбирает прямое объявление функции. Если вам требуется параметр в конструкторе, компилятор может увидеть, что это объявление переменной без каких-либо проблем. C c(4);Действительное объявление переменной.
Мартин Йорк

2

Судя по тому, что вы нам сказали, в valgrind это действительно ложное срабатывание. newСинтаксис с (), дорожа инициализировать объект, при условии , что это СТРУЧОК.

Возможно ли, что какая-то часть вашей структуры на самом деле не POD, и это препятствует ожидаемой инициализации? Можете ли вы упростить свой код и превратить его в пример для публикации, который по-прежнему указывает на ошибку valgrind?

В качестве альтернативы, возможно, ваш компилятор на самом деле не инициализирует структуры POD значениями.

В любом случае, вероятно, самое простое решение - написать конструктор (ы), необходимый для структуры / подразделов.


1

Я пишу тестовый код:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++ скомпилировать и запустить:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

Хорошо, я не так хорошо знаком с new sc;против new sc();. Я бы подумал, что отсутствующие скобки были ошибкой. Но это, кажется, демонстрирует, что ответ правильный (он выполняет инициализацию до 0, когда вы используете конструктор по умолчанию.)
nycynik

0

Вам необходимо инициализировать все члены вашей структуры, например:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

В этом случае я использую не класс, а структуру.
Shadow503

Структура - это класс. Отредактировал мой пост :)
ralphtheninja 06

2
Вам не нужно делать это для POD,
Алан Стоукс,

0

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


1
Будь то структура или класс, не имеет ничего общего с возможностью memset или создания конструктора. См., Например, stackoverflow.com/questions/2750270/cc-struct-vs-class
Эрик

Обычно вы бы не запомнили класс после его создания, поскольку его конструктор (предположительно) выполнял инициализацию.
Скотт Уилсон

1
в C ++ нет разницы между «классом» и «структурой», за исключением защиты по умолчанию (private и public, соответственно). Структура может быть не-POD, а класс может быть POD, эти концепции ортогональны.
Марк Мутц - mmutz

@mmutz и @Erik - согласились - сократили мой ответ, чтобы избежать путаницы.
Скотт Уилсон

memset () - не лучший вариант, вы можете обнулить структуру POD, просто вызвав ее ctor по умолчанию.
Nils

0

Это кажется мне самым простым способом. Члены структуры могут быть инициализированы с помощью фигурных скобок '{}'. Например, ниже приведена действительная инициализация.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

Есть хорошая информация о структурах в c ++ - https://www.geeksforgeeks.org/structures-in-cpp/

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.