Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
 
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
#1

Не могу заставить g++ не инициализировать локальную переменную нулем - C++

17.12.2015, 06:59. Просмотров 585. Ответов 20
Метки нет (Все метки)

Привет!
Есть такой код (из серии "что будет напечатано на экран")
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
 
int x = 1;
 
namespace A
{
int x = 2;
}
 
int main() 
{
    int x = x;
    cout << x << endl;
    return 0;
}
Ожидается мусор, но g++ и clang++ упорно выводят 0, мусор выводит только MSVC. Посмотрел ассемблерный выхлоп, g++ просто берет 4 байта со стека и печатает их (почему-то на стеке оказывается 0), clang++ явно пишет 0 на стек (т.е. явно инициализирует).
В g++ собирают так
Bash
1
g++ -Wall -pedantic-errors -std=c++11 -O2 -fno-init-local-zero tmp.cpp
варнинги просто чтоб посмотреть на уровень предупреждений, а стандарт указал, чтоб он gnu'шные фичи не использвоал (мало ли), оптимизация понятно для чего (чтоб не дебаг)). -fno-init-local-zero по сути используется по дефолту.

Как получить заветный мусор на выходе?
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
17.12.2015, 06:59     Не могу заставить g++ не инициализировать локальную переменную нулем
Посмотрите здесь:
C++ Не получается инициализировать переменную
C++ Инициализировать переменную ее максимально допустимым значением
Как (можно ли) инициализировать переменную в структуре? C++
C++ Возвращение ссылки на локальную переменную
Возврат ссылки на локальную переменную C++
C++ Требуется только объявить переменную, инициализировать ее не нужно
C++ VS2010 отладчик не видит локальную переменную
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Croessmah
Модератор
Эксперт CЭксперт С++
12979 / 7291 / 812
Регистрация: 27.09.2012
Сообщений: 18,007
Записей в блоге: 3
Завершенные тесты: 1
17.12.2015, 08:33     Не могу заставить g++ не инициализировать локальную переменную нулем #2
clang:
http://rextester.com/LPWKA67741
http://clang.llvm.org/compatibility.html

Non-initialization of __block variables

In the following example code, the x variable is used before it is defined:

C++
1
2
3
4
int f0() {
  __block int x;
  return ^(){ return x; }();
}
By an accident of implementation, GCC and llvm-gcc unintentionally always zero initialized __block variables. However, any program which depends on this behavior is relying on unspecified compiler behavior. Programs must explicitly initialize all local block variables before they are used, as with other local variables.

Clang does not zero initialize local block variables, and programs which rely on such behavior will most likely break when built with Clang.
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
17.12.2015, 10:37  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #3
Croessmah, я в clang другой вывод получал, правда на другом сайте и версию не помню.
Тогда может переформулировать вопрос - почему в g++ с разными ключами компиляции вывод всегда 0? Тупо случайность?
Croessmah
Модератор
Эксперт CЭксперт С++
12979 / 7291 / 812
Регистрация: 27.09.2012
Сообщений: 18,007
Записей в блоге: 3
Завершенные тесты: 1
17.12.2015, 10:46     Не могу заставить g++ не инициализировать локальную переменную нулем #4
Kastaneda, я как-то тоже хотел в этом разобраться, в общем, вот этот ответ более менее меня успокоил:
http://stackoverflow.com/questions/1...n-ubuntu-linux
Kerry_Jr
Модератор
Эксперт PHP
2162 / 1958 / 685
Регистрация: 14.05.2014
Сообщений: 5,734
Записей в блоге: 1
Завершенные тесты: 5
17.12.2015, 10:50     Не могу заставить g++ не инициализировать локальную переменную нулем #5
Kastaneda, если убрать оптимизацию, то скорее всего мусор будет. Сам недавно это заметил.
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
17.12.2015, 10:54  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #6
Croessmah, ну да, вот это
Why is the value of c zero? It could be that the compiler (g++) is zero-initializing the stack when the function is entered, to "help" your program to perform "correctly" even if it makes use of uninitialized variables. Or, it could be that the operating system is zero-initializing pages of memory before it gives them to your program. Or, perhaps a function that was called before main stored the value zero in the byte array now occupied by c, so it has a zero value.
похоже на правду.
Kerry_Jr, скорее должно быть наоборот.
Kerry_Jr
Модератор
Эксперт PHP
2162 / 1958 / 685
Регистрация: 14.05.2014
Сообщений: 5,734
Записей в блоге: 1
Завершенные тесты: 5
17.12.2015, 11:19     Не могу заставить g++ не инициализировать локальную переменную нулем #7
Цитата Сообщение от Kastaneda Посмотреть сообщение
скорее должно быть наоборот.
только что испробовал еще раз и убедился: без оптимизации - мусор, с оптимизацией - 0.
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
17.12.2015, 11:24  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #8
Цитата Сообщение от Kerry_Jr Посмотреть сообщение
только что испробовал еще раз и убедился: без оптимизации - мусор, с оптимизацией - 0.
А какие ключи компиляции и версия компилятора?
Вот мое
Bash
1
2
3
4
5
6
$ g++ -v
#бла бла бла
gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13) 
$ g++ tmp.cpp
$ ./a.out 
0
Croessmah
Модератор
Эксперт CЭксперт С++
12979 / 7291 / 812
Регистрация: 27.09.2012
Сообщений: 18,007
Записей в блоге: 3
Завершенные тесты: 1
17.12.2015, 11:44     Не могу заставить g++ не инициализировать локальную переменную нулем #9
Kastaneda, у тебя Linux? Ты про студию сказал, я думал ты про винду. Где-то натыкался на инфу, что Linux при первом выделении страницы памяти зачищает её нулями

Добавлено через 8 минут
Собственно:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
 
 
 
int main() 
{
    {
        int x = 10 ;
        std::cout << &x << " " << x << std::endl ;
    }
    int x ;
    std::cout << &x << " " << x << std::endl ;
    return 0;
}
http://rextester.com/MGIX49240
0x7ffcb5855a74 10
0x7ffcb5855a74 10
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
17.12.2015, 11:48  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #10
Цитата Сообщение от Croessmah Посмотреть сообщение
Kastaneda, у тебя Linux? Ты про студию сказал, я думал ты про винду
Да, я под линуксом, студийный компилятор в онлайне попробовал.
Цитата Сообщение от Croessmah Посмотреть сообщение
Собственно:
Тут другое, первый объект создается на стеке, потом разрашается, потом второй объект создается в том же месте, поэтому он содержит в себе значение предыдущего. Такое поведение будет в любом компиляторе (может с некоторой допилкой).
Croessmah
Модератор
Эксперт CЭксперт С++
12979 / 7291 / 812
Регистрация: 27.09.2012
Сообщений: 18,007
Записей в блоге: 3
Завершенные тесты: 1
17.12.2015, 12:00     Не могу заставить g++ не инициализировать локальную переменную нулем #11
Вот пример тогда другой:
C++
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
 
 
 
int main() 
{
    long int x ;
    std::cout << &x << " " << x << std::endl ;
}
http://rextester.com/ERC32092
0x7ffc514399f0 140721671871200
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
17.12.2015, 12:39  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #12
Цитата Сообщение от Croessmah Посмотреть сообщение
http://rextester.com/ERC32092
Вот это << &x убрать и будет 0 на выходе
Croessmah
Модератор
Эксперт CЭксперт С++
12979 / 7291 / 812
Регистрация: 27.09.2012
Сообщений: 18,007
Записей в блоге: 3
Завершенные тесты: 1
17.12.2015, 13:38     Не могу заставить g++ не инициализировать локальную переменную нулем #13
Kastaneda, я в курсе, но я заставил g++ мусор выводить
В любом случае, 0 - это тоже мусор и лучше всё инициализировать как положено.
hoggy
6368 / 2586 / 451
Регистрация: 15.11.2014
Сообщений: 5,714
Завершенные тесты: 1
17.12.2015, 19:55     Не могу заставить g++ не инициализировать локальную переменную нулем #14
Цитата Сообщение от Kastaneda Посмотреть сообщение
Тупо случайность?
линуксы любят чистить память процессов,
что бы лучше оберегаться от хаккеров.

поэтому, при первичном запуске память может быть девственной чистой - одни нули.
Evg
Эксперт CАвтор FAQ
17462 / 5700 / 361
Регистрация: 30.03.2009
Сообщений: 15,639
Записей в блоге: 26
18.12.2015, 17:43     Не могу заставить g++ не инициализировать локальную переменную нулем #15
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Если посмотрим на ассемблерный код, то увидим вот такое для компилятора gcc-4.8.0:

Код
...
main:
...
	movl	$0, 4(%esp)
	movl	$_ZSt4cout, (%esp)
	call	_ZNSolsEi
...
и вот такое для gcc-4.1.2

Код
...
main:
...
	<нет записей в %eax>
	movl	%eax, 4(%esp)
	movl	$_ZSt4cout, (%esp)
	call	_ZNSolsEi
...
Другими словами 0 для gcc-4.8.0 (и, видимо, более поздних версий) является никаким не мусором, а какой-то работой со стороны компилятора. Возможно, что это есть борьба с идиотами, которые кричат "на MSVS у меня всё работало, а на gcc не работает", не понимая, что в debug-версиях MSVS все локалы по умолчанию обнуляются (может это и не MSVS, а Borland, я не помню). Возможно, что это сделано для удобства самогО компилятора, чтобы ему не работать с неинициализированными переменными (с ними неудобно строить всякие потоковые графы и прочие конструкции, с которыми работает компилятор). Возможно, что ещё какая-то нанотехнология

Добавлено через 16 минут
Запустил с опцией -da, чтобы посмотреть промежуточное представление. Использовал sparc'овскую версию, т.к. она мне более понятна

--------------------------

Файл t.cc.192r.fwprop2

Код
<нет записей в reg 109>
...
(insn 5 2 6 2 (set (reg:DI 112 [ x+-4 ])
        (sign_extend:DI (reg/v:SI 109 [ x ]))) t.cc:14 145 {*sign_extendsidi2_insn}
     (expr_list:REG_DEAD (reg/v:SI 109 [ x ])
        (nil)))
...
(insn 8 7 9 2 (set (reg:DI 9 %o1)
        (reg:DI 112 [ x+-4 ])) t.cc:14 72 {*movdi_insn_sp64}
     (expr_list:REG_DEAD (reg:DI 112 [ x+-4 ])
        (nil)))
(call_insn 9 8 12 2 (parallel [
            (set (reg:DI 8 %o0)
                (call (mem:DI (symbol_ref:DI ("_ZNSolsEi")
...
insn 5 - пересылка неинициализированного виртуального регистра 109 в виртуального регистр 112
insn 8 - пересылка виртуального регистра 109 в физический регистр %o1 (формирование 2-го параметра call'а)
insn 9 - call

Т.е. тут пока ещё работаем с неициализированными значениями

--------------------------

Файл t.cc.194r.init-regs

Код
...
(insn 23 2 5 2 (set (reg/v:SI 109 [ x ])
        (const_int 0 [0])) t.cc:14 -1
     (nil))
(insn 5 23 6 2 (set (reg:DI 112 [ x+-4 ])
        (sign_extend:DI (reg/v:SI 109 [ x ]))) t.cc:14 145 {*sign_extendsidi2_insn}
     (expr_list:REG_DEAD (reg/v:SI 109 [ x ])
        (nil)))
...
(insn 8 7 9 2 (set (reg:DI 9 %o1)
        (reg:DI 112 [ x+-4 ])) t.cc:14 72 {*movdi_insn_sp64}
     (expr_list:REG_DEAD (reg:DI 112 [ x+-4 ])
        (nil)))
(call_insn 9 8 12 2 (parallel [
            (set (reg:DI 8 %o0)
                (call (mem:DI (symbol_ref:DI ("_ZNSolsEi")
...
Видим по сути всё то же самое, но появился insn 23, в котором выполняется инициализация нулём виртуального регистра 109

--------------------------

С учётом названия фазы "init-regs" похоже и вправду в компиляторе есть фаза заливания нулём всех неинициализированных виртуальных регистров

Добавлено через 9 минут
Исходники gcc-4.8.0. Файл gcc-4.8.0/gcc/init-regs.c

Комментарий в самом начале

C
/* Check all of the uses of pseudo variables.  If any use that is MUST
   uninitialized, add a store of 0 immediately before it.  For
   subregs, this makes combine happy.  For full word regs, this makes
   other optimizations, like the register allocator and the reg-stack
   happy as well as papers over some problems on the arm and other
   processors where certain isa constraints cannot be handled by gcc.
   These are of the form where two operands to an insn my not be the
   same.  The ra will only make them the same if they do not
   interfere, and this can only happen if one is not initialized.
 
   There is also the unfortunate consequence that this may mask some
   buggy programs where people forget to initialize stack variable.
   Any programmer with half a brain would look at the uninitialized
   variable warnings.  */
Условие применимости

C
static bool
gate_initialize_regs (void)
{
  return optimize > 0;
}
Для сравнения заглянул в соседний файл dce.c (dead code elimination). В нём условие срабатывания вот такое

C
static bool
gate_fast_dce (void)
{
  return optimize > 0 && flag_dce
    && dbg_cnt (dce_fast);
}
Похоже на то, что для отключения dce опция у компилятора есть, а отключить init-regs нельзя, только в виде отключения оптимизаций

Добавлено через 15 минут
Цитата Сообщение от Kastaneda Посмотреть сообщение
Вот это << &x убрать и будет 0 на выходе
С учётом вышенаписанного тут попросту не применяется init-regs, т.к. переменная попадает не на виртуальный регистр, а напрямую в стек (из-за взятия адреса)

Добавлено через 2 минуты
Цитата Сообщение от Croessmah Посмотреть сообщение
Kastaneda, я как-то тоже хотел в этом разобраться, в общем, вот этот ответ более менее меня успокоил:
http://stackoverflow.com/questions/1...n-ubuntu-linux
Про инициализацию стека - полная бредятина, пример из поста #11 налгядно это демонстрирует
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
19.12.2015, 04:41  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #16
Спасибо, теперь понятно
Цитата Сообщение от Evg Посмотреть сообщение
и вот такое для gcc-4.1.2
У меня в 4.9 такой же код генерится.
Evg
Эксперт CАвтор FAQ
17462 / 5700 / 361
Регистрация: 30.03.2009
Сообщений: 15,639
Записей в блоге: 26
19.12.2015, 12:54     Не могу заставить g++ не инициализировать локальную переменную нулем #17
gcc-4.9 у меня только 64-битный компилятор, там так:

Код
        xorl    %esi, %esi  <-- обнуление
        movl    $_ZSt4cout, %edi
        call    _ZNSolsEi
Добавлено через 2 минуты
На gcc-4.9 в режиме 32 компилятор работает, просто нету хидеров. Аналогичный код на Си даёт такой ассемблер:

Код
        pushl   $0
        pushl   $.LC0
        call    printf
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
19.12.2015, 13:57  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #18
Станно, вот у меня в 4.9.2
Assembler
1
2
3
4
5
6
7
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
обнуления нет

Добавлено через 5 минут
Evg, не умею читать лиспоподобную портянку (похоже это AST?), которую ты показал выше (-da), но в файле tmp.cpp.236r.stack нашел такое
Код
(insn 5 2 6 2 (set (reg:SI 0 ax [85])
        (mem/c:SI (plus:DI (reg/f:DI 6 bp)
                (const_int -4 [0xfffffffffffffffc])) [0 x+0 S4 A32])) tmp.cpp:6 90 {*movsi_internal}
     (nil))
Чтоб было понятно, что такое tmp.cpp:6 вот мой код
C++
1
2
3
4
5
6
7
8
#include <iostream>
 
int main() 
{
    int x = x;
    std::cout << x;
    return 0;
}
вот это (reg:SI 0 ax [85]) интуитивно напоминает обнуление, не?
Evg
Эксперт CАвтор FAQ
17462 / 5700 / 361
Регистрация: 30.03.2009
Сообщений: 15,639
Записей в блоге: 26
19.12.2015, 14:38     Не могу заставить g++ не инициализировать локальную переменную нулем #19
Цитата Сообщение от Kastaneda Посмотреть сообщение
Evg, не умею читать лиспоподобную портянку (похоже это AST?)
Это rtl. Один узел представления (insn) является как бы деревом. Если отформатировать, то выглядит так

Код
set (reg:SI 0 ax [85])
    (mem/c:SI (plus:DI (reg/f:DI 6 bp)
                       (const_int -4 [0xfffffffffffffffc]))
Т.е. записать в reg85 (%ax) значение из адреса reg6 (%bp) плюс (-4). Т.е. из стека со смещением -4 считывается значение и записывается в регистр. Видимо, переменная x распределилась в стеке, может ты оптимизации не включил?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
19.12.2015, 14:40     Не могу заставить g++ не инициализировать локальную переменную нулем
Еще ссылки по теме:
C++ Передать локальную переменную в новую функцию
Ошибка E2363 Попытка возвратить локальную переменную C++
Как инициализировать глобальную переменную в зависимости от количества строк в файле C++
Как в данном контексте правильно разместить статическую локальную переменную? C++
Как локальную переменную сделать глобально, при это сохранив накопленное в ней значение? C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Kastaneda
Форумчанин
Эксперт С++
4479 / 2841 / 226
Регистрация: 12.12.2009
Сообщений: 7,222
Записей в блоге: 1
Завершенные тесты: 1
19.12.2015, 14:40  [ТС]     Не могу заставить g++ не инициализировать локальную переменную нулем #20
Цитата Сообщение от Evg Посмотреть сообщение
может ты оптимизации не включил?
да, не включил, специально, чтоб посмотреть будет ли обнуляться.
Yandex
Объявления
19.12.2015, 14:40     Не могу заставить g++ не инициализировать локальную переменную нулем
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru