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

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

Исходя из того, что я прочитал, кажется, что:

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

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

myStruct = new MyStruct();

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


person Shadow503    schedule 06.05.2011    source источник
comment
также обратите внимание, что new MyStruct() не требовалось устанавливать какие-либо байты заполнения в C ++ 03. В C ++ 0x это так. Любые биты заполнения будут установлены в 0 в C ++ 0x.   -  person Johannes Schaub - litb    schedule 06.05.2011
comment
Спасибо за улов, отредактировал свой вопрос.   -  person Shadow503    schedule 06.05.2011
comment
Повторный FAQ: stackoverflow.com/questions/620137/   -  person Robᵩ    schedule 06.05.2011
comment
Насколько я понимаю, вы не должны получать это сообщение об ошибке. Возможно, ваша структура данных не является POD? Можете ли вы сократить свой код до самой маленькой программы, которая все еще демонстрирует это поведение, и опубликовать эту дистиллированную программу? (См. sscce.org).   -  person Robᵩ    schedule 06.05.2011
comment
Какой компилятор? Вы можете опубликовать определение MyStruct?   -  person Alan Stokes    schedule 06.05.2011
comment
ах, теперь я понимаю, что вы имели в виду изначально.   -  person Johannes Schaub - litb    schedule 06.05.2011
comment
Эта программа соответствует вашему описанию, но в Ubuntu LTS 10.04.2 не вызывает описанных вами ошибок.   -  person Robᵩ    schedule 06.05.2011


Ответы (6)


В 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();

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

person Martin York    schedule 06.05.2011
comment
У вас есть справочник по этой нулевой инициализации переменных в C ++? Последнее, что я слышал, он по-прежнему ведет себя так же, как C. IIRC, компилятор MSVC в какой-то момент даже отбросил неинициализированные элементы с нулевой инициализацией (я полагаю, из соображений производительности), что, по-видимому, создало довольно серьезные проблемы для других подразделений MS :) - person Marc Mutz - mmutz; 06.05.2011
comment
@mmutz: Вы имеете в виду C c = {0}; - person Martin York; 06.05.2011
comment
нет, C(), но кажется вы правы. - person Marc Mutz - mmutz; 06.05.2011
comment
В классе POD T, T() всегда инициализируют нулем скалярные подобъекты. Имя инициализации верхнего уровня в C ++ 98 называлось иначе, чем в C ++ 03 (инициализация по умолчанию против инициализации значения), но конечный эффект для POD такой же. И в C ++ 98, и в C ++ 03 все значения будут равны нулю. - person Johannes Schaub - litb; 06.05.2011
comment
Как вызвать таким образом ctor по умолчанию для struct sigaction из #include <signal.h> POSIX? эта структура имеет то же имя, что и функция sigaction - person yanpas; 16.08.2016
comment
@yanpas Поскольку это класс C, у него нет конструктора. Это может быть инициализированное значение или нулевое значение. Вы также можете использовать исходный синтаксис C для инициализации структуры. struct sigaction actionObject = {func, other1, other2}; - person Martin York; 16.08.2016
comment
Если это класс C и у него нет ctor, вызовет его нулевой конструктор по умолчанию, инициализирует его? - person yanpas; 16.08.2016
comment
@yanpas У него нет конструктора для вызова. Значит, вы не можете вызвать конструктор по умолчанию. НО нулевая инициализация - это особый режим инициализации объектов, описываемый стандартом. Это происходит в очень специфических ситуациях (как описано выше). Хотя я скажу вам, что это выглядит точно так же, как вызов конструктора по умолчанию. - person Martin York; 16.08.2016
comment
Можете ли вы показать рекомендуемый синтаксис для передачи инициализированного нулем POD в качестве аргумента функции? Не уверен, но возможно ... (C){0}, но приведение к типу C выглядит странно. - person kevinarpe; 12.03.2017
comment
Если структура имеет более одного элемента, C c = { 0 } вызывает предупреждение с -Wextra, в отличие от C c = {}. Последнее также указано в стандарте: en.cppreference.com/w/cpp/language / zero_initialization. - person krlmlr; 25.07.2017
comment
@yanpas Я думаю, что соответствующий раздел стандарта - [dcl.init.list] (пункт списка 3.4) - person MattR; 26.10.2017
comment
@MartinYork, меня смущает ваш последний фрагмент кода и приложенные утверждения. Разве C c(); на самом деле не прямая инициализация в стеке с использованием конструктора по умолчанию без параметров? А C c = C() называется инициализацией копирования? Пожалуйста, проверьте этот вопрос, который объясняет два типа инициализации. Не могли бы вы объяснить, к сожалению, это не объявление переменной. в комментарии или изменив свой ответ? - person horiac7; 15.08.2018
comment
@RockyRaccoon C c();, к сожалению, не является объявлением переменной, а скорее предварительным объявлением функции (функция с именем 'c', которая не принимает параметров и возвращает объект типа C). Google: самый неприятный синтаксический анализ - person Martin York; 15.08.2018
comment
@RockyRaccoon Объявление C c = C() на самом деле не является копией-инициализацией (хотя оно действительно выглядит так же (и я тоже изначально (и неправильно), хотя и таким образом); я думал, что его компилятор был достаточно умен, чтобы оптимизировать копию, поэтому вы никогда не видел копию), НО это явно вызывается в стандарте только как конструкция. - person Martin York; 15.08.2018
comment
@RockyRaccoon Разница здесь и в вопросе, на который вы ссылаетесь в своем комментарии; в том, что в этом случае в конструкторе не используются параметры. Таким образом, компилятору трудно проанализировать это и определить разницу между объявлением переменной и предварительным объявлением функции. Он выбирает прямое объявление функции. Если вам требуется параметр в конструкторе, компилятор может увидеть, что это объявление переменной без каких-либо проблем. C c(4); Действительное объявление переменной. - person Martin York; 15.08.2018
comment
@MartinYork, спасибо за ответы. Теперь я понимаю суть проблемы. Я также читал вики «Самый неприятный разбор». В свете этого - считаете ли вы, что всегда следует использовать copy-initialization при инициализации локальных объектов, поскольку он не подвержен этим ошибкам (ловушкам)? Я также понимаю, что инициализация копирования не выполняет дополнительных операций по вызову конструктора дополнительного копирования - поскольку в большинстве случаев он оптимизируется компилятором, если вы не отключите copy-elision (флаг G ++ -fno-elide-constructors). - person horiac7; 15.08.2018
comment
@RockyRaccoon Вот почему в язык была добавлена ​​инициализация {}. C c{}; недвусмысленен и делает то, что вы ожидаете. - person Martin York; 15.08.2018
comment
@MartinYork, это все еще не однозначно, так как вам нужно знать о классах, которые реализуют конструктор initializer_list - тогда он имеет приоритет над другими конструкторами и не ведет себя как использующий старый синтаксис: std::vector<int> v1(3, 0); и std::vector<int> v2 = std::vector<int>(3, 0); приводят к векторам с тремя элементами 0 каждый в то время как std::vector<int> v3{ 3, 0 }; и std::vector<int> v4 = std::vector<int>{ 3, 0 }; будут инициализировать векторы двумя элементами, 3 и 0. Так есть ли другой метод, который действительно является звуковым? - person horiac7; 16.08.2018
comment
@RockyRaccoon Вы сбиваете с толку свои конструкторы. Этот вопрос касается только конструкции по умолчанию. Конечно, ваш класс может перекрыть конструктор с помощью std:: initializer_list, но пустой список инициализаторов и конструктор по умолчанию (как вы надеетесь) логически делают то же самое. Но именно поэтому у нас также есть обзор кода. - person Martin York; 17.08.2018

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

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

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

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

person Mark B    schedule 06.05.2011

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

#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
person Yadong    schedule 21.01.2017
comment
Хорошо, я не так хорошо знаком с new sc; vs new sc();. Я бы подумал, что отсутствующие скобки были ошибкой. Но это, кажется, демонстрирует, что ответ правильный (он инициализируется до 0, когда вы используете конструктор по умолчанию). - person nycynik; 10.04.2019

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

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

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

};
person ralphtheninja    schedule 06.05.2011
comment
В этом случае я использую не класс, а структуру. - person Shadow503; 06.05.2011
comment
Структура - это класс. Отредактировал мой пост :) - person ralphtheninja; 06.05.2011
comment
Для POD этого делать не нужно. - person Alan Stokes; 06.05.2011
comment
Как у структуры есть частные члены? - person OS2; 11.01.2021
comment
Что значит как? Добавляя приват перед ними, как обычный класс. - person ralphtheninja; 11.01.2021

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

person Scott C Wilson    schedule 06.05.2011
comment
Будь то структура или класс, это не имеет ничего общего с возможностью memset или создания конструктора. См., Например, stackoverflow.com/questions/2750270/c-c-struct-vs-class - person Erik; 06.05.2011
comment
Обычно вы не будете использовать memset для класса после его создания, поскольку его конструктор (предположительно) выполнял инициализацию. - person Scott C Wilson; 06.05.2011
comment
в C ++ нет разницы между классом и структурой, за исключением защиты по умолчанию (private и public, соответственно). Структура может быть не-POD, а класс может быть POD, эти концепции ортогональны. - person Marc Mutz - mmutz; 06.05.2011
comment
@mmutz и @Erik - согласились - сократили мой ответ, чтобы избежать путаницы. - person Scott C Wilson; 06.05.2011
comment
memset () - не лучший вариант, вы можете обнулить структуру POD, просто вызвав ее ctor по умолчанию. - person Nils; 20.08.2012

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

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/

person Michael Klishevich    schedule 26.01.2020