LINUX.ORG.RU

gcc странно инициализирует std::vector внутри структурки

 , , ,


1

8

(На работе меня игнорируют, так что спрошу здесь.)

Требуются люди со скиллом C++ Standard Interfacing V.

Explain:

$ cat cpp.cpp
#include <vector>

struct A
{
    std::vector<int> v;
};

struct B
{
    B()
#ifdef WITH_INIT
    : a()
#endif
    {}

    A a;
};

int main()
{
    B b;
    b.a.v.push_back(42);
}
$ g++ -O0 -S -o cpp.s.0 cpp.cpp

$ g++ -O0 -S -o cpp.s.1 cpp.cpp -DWITH_INIT

$ diff -u cpp.s.0 cpp.s.1
--- cpp.s.0   2014-08-26 15:42:19.635177434 +0300
+++ cpp.s.1   2014-08-26 15:42:25.295177509 +0300
@@ -80,6 +80,12 @@
    subq   $16, %rsp
    movq   %rdi, -8(%rbp)
    movq   -8(%rbp), %rax
+   movq   $0, (%rax)
+   movq   -8(%rbp), %rax
+   movq   $0, 8(%rax)
+   movq   -8(%rbp), %rax
+   movq   $0, 16(%rax)
+   movq   -8(%rbp), %rax
    movq   %rax, %rdi
    call   _ZN1AC1Ev
    leave 

Мне интересно, это gcc/libstdc++ бажит, или там нечто в стандарте позволяет так делать?

★★★

Ответ на: комментарий от ilammy

Он хочет сказать, что это может быть из-за каких-то оптимизаций.

Kotolegokot
()

Мне интересно, это gcc/libstdc++ бажит, или там нечто в стандарте позволяет так делать?

А что здесь неправильно? Занулил он память объекта, прежде чем вызвать конструктор для него, хуже же то от этого не будет.

Explain

Конструктор по умолчанию не обязан инициализировать типы с POD полями, если нету (). Если сделать так:

 #include <iostream>                                                                                                                
 #include <vector>                                                                                                                  
                                                                                                                                    
 struct A                                                                                                                           
 {                                                                                                                                  
     int a;                                                                                                                         
     std::vector<int> v;                                                                                                            
 };                                                                                                                                 
                                                                                                                                    
 struct B                                                                                                                           
 {                                                                                                                                  
     B()                                                                                                                            
 #ifdef WITH_INIT                                                                                                                   
     : a()                                                                                                                          
 #endif                                                                                                                             
     {                                                                                                                              
         std::cout << a.a << std::endl;                                                                                             
     }                                                                                                                              
                                                                                                                                    
     A a;                                                                                                                           
 };                                                                                                                                 
                                                                                                                                    
 int main()                                                                                                                         
 {                                                                                                                                  
     B b;                                                                                                                           
     b.a.v.push_back(42);                                                                                                           
 }                                                                                                                                  

То вариант без инициализации у меня выводит 1, а с ней 0. Заполнение всего объекта нулями при наличии скобок это самый простой способ инициализации всех полей (при оптимизациях выполняется анализ структуры, а не в тупую заполнение её нулями).

Вот неплохое демо по данному вопросу.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Кстати, если мне не изменяет память, студийный компилятор также зануляет некоторые области памяти в debug сборке, что иногда чревато падением release сборки на том месте, где в debug поток выполнения шел нормально.

Shadow1251
()
Ответ на: комментарий от Shadow1251

Как раз, чтобы оно падало, он любит забивать память байтами вроде 0xCC в debug сборке. Может раньше нули были, или сам отладчик пишет не нули, потому я что-то подобное тоже помню.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Оно как-то интересно. Вроде и 0xcd забивает, но в то же время часто все валилось (ССЗБ, но речь не о том) именно из-за нулей.

Pavval ★★★★★
()
Ответ на: комментарий от xaizek

Окей, я вроде понял. [линк]

Есть три варианта инициализации по-умолчанию: zero, default и value. По пункту 12.6.2/8, для нестатических полей класса, не упомянутых в списке инициализации, применяется default-инициализация. Явное упоминание поля с пустыми скобками в списке инициализации применяет пункт 8.5/10, по которому данное поле должно быть value-инициализировано.

То есть, без списка инициализации struct A инициализируется 8.5/6:1 — потому что это тип-класс, то у него сразу вызывается конструктор по-умолчанию (который вызывает конструктор вектора).

Со списком инициализации struct A инициализируется по 8.5/7:2 — потому что это тип-класс без пользовательского конструктора по умолчанию, то всё сначала зануляется, потом вызывается конструктор по-умолчанию (который вызывает конструктор вектора).

То есть в итоге моя проблема была в том, что конструктор вектора, видимо, не инициализировал свои поля... Потому что полез я с этим разбираться тогда, когда попробовал собрать приложение с дебажными ключами без оптимизаций, а оно упало на первом же добавлении в вектор, хотя до этого отлично работало с -O2. При включенных оптимизациях всё это инлайнится и кусочек памяти под вектор инициализировался корректно.

ilammy ★★★
() автор топика
Ответ на: комментарий от nanoolinux

Есть переменные, просто они бывают связанными и не связанными. Отсутствует деструктивное присваивание.

NegatiV
()
Ответ на: комментарий от ilammy

То есть в итоге моя проблема была в том, что конструктор вектора, видимо, не инициализировал свои поля... Потому что полез я с этим разбираться тогда, когда попробовал собрать приложение с дебажными ключами без оптимизаций, а оно упало на первом же добавлении в вектор, хотя до этого отлично работало с -O2. При включенных оптимизациях всё это инлайнится и кусочек памяти под вектор инициализировался корректно.

Ниасилил. У тебя код этого треда падал??

Pavval ★★★★★
()
Ответ на: комментарий от ilammy

упало на первом же добавлении в вектор

Как такое случилось?

Shadow1251
()
Ответ на: комментарий от Pavval

Конкретно этот — нет. А там отличий не особо много, только структурка создаётся в куче, некоторое время её никто не трогает, а потом приходит сообщение от сервера и в вектор надо вставить чиселку.

ilammy ★★★
() автор топика
Ответ на: комментарий от ilammy

А куча под структуру и под хранилище вектора остается валидной? Может ее кто-то портит.
Просто интересно как происходит падение при записи в вектор :)

Shadow1251
()
Ответ на: комментарий от NegatiV

Сказал бы лучше константные переменные, понятней было бы.

nanoolinux ★★★★
()
Ответ на: комментарий от ilammy

А там отличий не особо много, только структурка создаётся в куче, некоторое время её никто не трогает, а потом приходит сообщение от сервера и в вектор надо вставить чиселку.

И падает у тебя именно из-за отличий. А вектор так или иначе нормально инициализируется. Так что без внешних повреждений памяти или чего-то подобного у тебя ничего не упадет.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Ну да. Фраза одна и та же. Я хоть в контексте топика эрланг упоминаю, а ты просто кривляешься. Причём всё время одинаково)

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

Я хоть в контексте топика эрланг упоминаю

Даа. В топиках конкретные вопросы по С++, а ты оффтопишь про ерланг ни к селу, ни к городу, и называешь этот оффтоп «в контексте топика». Другой язык имеет смысл обсуждать только, если идет речь о старте какого-то проекта - и всё. Советы же «все взять и переписать» (явные или нет) - или дебилизм, или толстый троллинг.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Вроде? Все гораздо хуже:

Value     Name           Description 
------   --------        -------------------------
0xCD     Clean Memory    Allocated memory via malloc or new but never 
                         written by the application. 

0xDD     Dead Memory     Memory that has been released with delete or free. 
                         Used to detect writing through dangling pointers. 

0xED or  Aligned Fence   'No man's land' for aligned allocations. Using a 
0xBD                     different value here than 0xFD allows the runtime
                         to detect not only writing outside the allocation,
                         but to also detect mixing alignment-specific
                         allocation/deallocation routines with the regular
                         ones.

0xFD     Fence Memory    Also known as "no mans land." This is used to wrap 
                         the allocated memory (surrounding it with a fence) 
                         and is used to detect indexing arrays out of 
                         bounds or other accesses (especially writes) past
                         the end (or start) of an allocated block.

0xFD or  Buffer slack    Used to fill slack space in some memory buffers 
0xFE                     (unused parts of `std::string` or the user buffer 
                         passed to `fread()`). 0xFD is used in VS 2005 (maybe 
                         some prior versions, too), 0xFE is used in VS 2008 
                         and later.

0xCC                     When the code is compiled with the /GZ option,
                         uninitialized variables are automatically assigned 
                         to this value (at byte level). 


// the following magic values are done by the OS, not the C runtime:

0xAB  (Allocated Block?) Memory allocated by LocalAlloc(). 

0xBAADF00D Bad Food      Memory allocated by LocalAlloc() with LMEM_FIXED,but 
                         not yet written to. 

0xFEEEFEEE               OS fill heap memory, which was marked for usage, 
                         but wasn't allocated by HeapAlloc() or LocalAlloc(). 
                         Or that memory just has been freed by HeapFree(). 

slackwarrior ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.